├── conf.env ├── Dockerfile ├── README.md └── dtrctl.sh /conf.env: -------------------------------------------------------------------------------- 1 | ## PARAMETERS to reach the SOURCE DTR 2 | SRC_DTR_URL= 3 | SRC_DTR_USER= 4 | SRC_DTR_PASSWORD= 5 | SRC_NO_OF_REPOS=1000 #Default value, can change depending on number of repos 6 | 7 | ## PARAMETERS to reach the DESTINATION DTR 8 | DEST_DTR_URL= 9 | DEST_DTR_USER= 10 | DEST_DTR_PASSWORD= 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | MAINTAINER Mark Church 4 | 5 | RUN apt-get update &&\ 6 | apt-get install -y apt-transport-https ca-certificates curl jq &&\ 7 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - &&\ 8 | echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable edge" > /etc/apt/sources.list.d/docker.list &&\ 9 | apt-get update &&\ 10 | apt-get install -y docker-ce &&\ 11 | rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/docker.list 12 | 13 | RUN mkdir /dtrctl 14 | 15 | COPY dtrctl.sh /dtrctl/dtrctl.sh 16 | 17 | WORKDIR /dtrctl 18 | 19 | ENTRYPOINT ["/dtrctl.sh"] 20 | 21 | CMD ["--help"] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dtrctl 2 | 3 | A tool that can do different kinds of operations to a Docker Trusted Registry. Some of these tasks include: 4 | 5 | - Pulling a DTR's org, repo, and team structure locally to inspect 6 | - Syncing org, repo, team, and repo access rights from a source DTR to a destination DTR 7 | - Syncing images from a source DTR to a destination DTR 8 | - Printing out a map of a DTR's team to repo memberships 9 | 10 | ### Usage 11 | ``` 12 | ./dtrctl.sh --help 13 | 14 | Usage: dtrctl COMMAND(s) 15 | Pronounced: dtr-control 16 | 17 | Options 18 | 19 | 20 | -s, --source-metadata Pull org, repo, team, and team access metadata from source DTR and store locally 21 | -p, --push-metadata Push org, repo, team, and team access metadata from local source to dest DTR 22 | -i, --image-sync Pull images from source DTR and push to dest DTR 23 | -a, --print-access Print mapping of access rights between teams and repos 24 | --help Print usage 25 | ``` 26 | 27 | ### Configuration file format 28 | 29 | ``` 30 | ## PARAMETERS to reach the SRC DTR 31 | SRC_DTR_URL= 32 | SRC_DTR_USER= 33 | SRC_DTR_PASSWORD= 34 | SRC_NO_OF_REPOS=1000 #Default value 35 | 36 | ## PARAMETERS to reach the DESTINATION DTR 37 | DEST_DTR_URL= 38 | DEST_DTR_USER= 39 | DEST_DTR_PASSWORD= 40 | ``` 41 | 42 | Requirements 43 | 44 | ## Examples 45 | 46 | ### Pulling the metadata locally 47 | 48 | The `-s` flag will sync the source DTR metadata locally. The metadata will be placed in the container at `/dtrsync` which can be mounted locally. 49 | 50 | 51 | ``` 52 | docker run --rm -it \ 53 | -v /var/run/docker.sock:/var/run/docker.sock \ 54 | -v /etc/docker:/etc/docker \ 55 | -v ~/dtrsync:/dtrsync \ 56 | --env-file conf.env \ 57 | chrch/dtrctl -s 58 | ``` 59 | 60 | The following volumes are required so that Docker can function inside the contianer. 61 | ``` 62 | -v /var/run/docker.sock:/var/run/docker.sock \ 63 | -v /etc/docker:/etc/docker 64 | ``` 65 | 66 | The following volumes are configureable and specify the output location of the DTR metadata and also the location of the configuration env variables. 67 | 68 | ``` 69 | -v ~/dtrsync:/dtrsync \ 70 | --env-file conf.env \ 71 | ``` 72 | 73 | Once the metadata is pulled locally its structure will look like this: 74 | 75 | ``` 76 | $ tree ~/dtrsync/ 77 | ├── docker-datacenter 78 | │   ├── repoConfig 79 | │   └── teamConfig 80 | ├── org1 81 | │   ├── repoConfig 82 | │   ├── t1 83 | │   │   ├── members 84 | │   │   └── repoAccess 85 | │   ├── t2 86 | │   │   ├── members 87 | │   │   └── repoAccess 88 | │   └── teamConfig 89 | ├── org2 90 | │   ├── repoConfig 91 | │   └── teamConfig 92 | ├── org3 93 | │   ├── repoConfig 94 | │   ├── t3 95 | │   │   ├── members 96 | │   │   └── repoAccess 97 | │   └── teamConfig 98 | ├── orgConfig 99 | └── orgList 100 | ... 101 | ``` 102 | 103 | ### Push org/team/repo metadata to dest DTR 104 | 105 | ``` 106 | docker run --rm -it \ 107 | -v /var/run/docker.sock:/var/run/docker.sock \ 108 | -v /etc/docker:/etc/docker \ 109 | -v ~/dtrsync:/dtrsync \ 110 | --env-file conf.env \ 111 | chrch/dtrctl -p 112 | ``` 113 | 114 | ### Sync org metadata and images from a source DTR to a destination DTR 115 | 116 | ``` 117 | docker run --rm -it \ 118 | -v /var/run/docker.sock:/var/run/docker.sock \ 119 | -v /etc/docker:/etc/docker \ 120 | -v ~/dtrsync:/dtrsync \ 121 | --env-file conf.env \ 122 | chrch/dtrctl -i 123 | ``` 124 | 125 | 126 | ### Develop dtrctl locally 127 | ``` 128 | $ git clone https://github.com/mark-church/dtrctl.git 129 | 130 | $ cd ~/dtrctl 131 | 132 | $ docker run --rm -it \ 133 | -v /var/run/docker.sock:/var/run/docker.sock \ 134 | -v /etc/docker:/etc/docker \ 135 | -v ~/dtrsync:/dtrsync \ 136 | --env-file conf.env \ 137 | -v ~/dtrctl:/dtrctl \ 138 | chrch/dtrctl 139 | ``` 140 | 141 | 142 | -------------------------------------------------------------------------------- /dtrctl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | main() { 4 | echo "Starting dtrctl ..." 5 | authenticate 6 | 7 | if [ "$PULL" ]; then 8 | if [ ! $SRC_DTR_URL ]; then 9 | echo "error: No source DTR specified" 10 | exit 1 11 | fi 12 | 13 | getOrgs 14 | getRepos 15 | getTeams 16 | getTeamMembers 17 | getTeamRepoAccess 18 | echo "Sync from source DTR to local copy complete" 19 | fi 20 | 21 | if [ "$PUSH" ]; then 22 | if [ ! $DEST_DTR_URL ]; then 23 | echo "error: No destination DTR specified" 24 | exit 1 25 | fi 26 | 27 | putOrgs 28 | putRepos 29 | putTeams 30 | putTeamMembers 31 | putTeamRepoAccess 32 | echo "Sync from local copy to destination DTR complete" 33 | 34 | fi 35 | 36 | if [ "$SYNC_IMAGES" ]; then 37 | migrateImages 38 | echo "Image migration from source DTR to destination DTR complete" 39 | fi 40 | 41 | if [ "$PRINT_ACCESS" ]; then 42 | printAccessMap 43 | fi 44 | } 45 | 46 | authenticate() { 47 | 48 | if [ $SRC_DTR_URL ]; then 49 | echo "Authenticating and logging in to $SRC_DTR_URL" 50 | if [ ! -d "/etc/docker/certs.d/${SRC_DTR_URL}" ]; then 51 | mkdir -p /etc/docker/certs.d/"${SRC_DTR_URL}" 52 | fi 53 | 54 | curl -ksf https://"${SRC_DTR_URL}"/ca > /etc/docker/certs.d/"${SRC_DTR_URL}"/ca.crt 55 | openssl s_client -host "${SRC_DTR_URL}" -port 443 /dev/null | openssl x509 -outform PEM > /etc/docker/certs.d/"${SRC_DTR_URL}"/ca.crt 56 | 57 | docker login "$SRC_DTR_URL" -u "$SRC_DTR_USER" -p "$SRC_DTR_PASSWORD" 58 | SRC_DTR_TOKEN=$(cat ~/.docker/config.json | jq -r ".auths[\"$SRC_DTR_URL\"].identitytoken") 59 | fi 60 | 61 | 62 | if [ $DEST_DTR_URL ]; then 63 | echo "Authenticating and logging in to $DEST_DTR_URL" 64 | if [ ! -d "/etc/docker/certs.d/${DEST_DTR_URL}" ]; then 65 | mkdir -p /etc/docker/certs.d/"${DEST_DTR_URL}" 66 | fi 67 | 68 | curl -ksf https://"${DEST_DTR_URL}"/ca > /etc/docker/certs.d/"${DEST_DTR_URL}"/ca.crt 69 | openssl s_client -host "${DEST_DTR_URL}" -port 443 /dev/null | openssl x509 -outform PEM > /etc/docker/certs.d/"${DEST_DTR_URL}"/ca.crt 70 | 71 | docker login "$DEST_DTR_URL" -u "$DEST_DTR_USER" -p "$DEST_DTR_PASSWORD" 72 | DEST_DTR_TOKEN=$(cat ~/.docker/config.json | jq -r ".auths[\"$DEST_DTR_URL\"].identitytoken") 73 | fi 74 | } 75 | 76 | 77 | 78 | ########################### 79 | # GET # 80 | ########################### 81 | 82 | getOrgs() { 83 | curl -s --insecure \ 84 | https://"$SRC_DTR_URL"/enzi/v0/accounts?refresh_token="$SRC_DTR_TOKEN" | \ 85 | jq -c '.accounts[] | select(.isOrg==true) | {name: .name, fullName: .fullName, isOrg: .isOrg}' \ 86 | > orgConfig 87 | 88 | cat orgConfig | jq -r '.name' > orgList 89 | 90 | cat orgList | while IFS= read -r i; 91 | do 92 | if [ ! -d ./$i ]; then 93 | mkdir ./$i 94 | fi 95 | done 96 | } 97 | 98 | getRepos() { 99 | cat orgList | sort -u | while IFS= read -r i; 100 | do 101 | curl -s --insecure \ 102 | https://"$SRC_DTR_URL"/api/v0/repositories/$i?refresh_token="$SRC_DTR_TOKEN" | \ 103 | jq '.repositories[] | {name: .name, shortDescription: .shortDescription, longDescription: "", visibility: .visibility}' \ 104 | > ./$i/repoConfig 105 | done 106 | } 107 | 108 | 109 | getTeams() { 110 | cat orgList | sort -u | while IFS= read -r i; 111 | do 112 | curl -s --insecure \ 113 | https://"$SRC_DTR_URL"/enzi/v0/accounts/$i/teams?refresh_token="$SRC_DTR_TOKEN" | jq -c '.teams[] | {name: .name, description: .description}' > ./$i/teamConfig 114 | 115 | cat ./$i/teamConfig | while IFS= read -r j; 116 | do 117 | if [ ! -d ./$i/$(echo $j | jq -r '.name') ]; then 118 | mkdir ./$i/$(echo $j | jq -r '.name') 119 | fi 120 | done 121 | done 122 | } 123 | 124 | getTeamMembers() { 125 | cat orgList | sort -u | while IFS= read -r i; 126 | do 127 | cat ./$i/teamConfig | jq -r '.name' | while IFS= read -r j; 128 | do 129 | curl -s --insecure \ 130 | https://"$SRC_DTR_URL"/enzi/v0/accounts/${i}/teams/${j}/members?refresh_token="$SRC_DTR_TOKEN" | jq -c '.members[] | {name: .member.name, isAdmin: .isAdmin, isPublic: .isPublic}' \ 131 | > ./$i/$j/members 132 | done 133 | done 134 | } 135 | 136 | 137 | getTeamRepoAccess() { 138 | cat orgList | sort -u | while IFS= read -r i; 139 | do 140 | cat ./$i/teamConfig | jq -r '.name' | while IFS= read -r j; 141 | do 142 | curl -s --insecure \ 143 | https://"$SRC_DTR_URL"/api/v0/accounts/${i}/teams/${j}/repositoryAccess?refresh_token="$SRC_DTR_TOKEN" | jq -c '.repositoryAccessList[]' > ./$i/$j/repoAccess 144 | done 145 | done 146 | } 147 | 148 | 149 | 150 | ########################### 151 | # PUT # 152 | ########################### 153 | 154 | putOrgs() { 155 | cat orgConfig | while IFS= read -r i; 156 | do 157 | curl --insecure -X POST --header "Content-Type: application/json" \ 158 | --header "Accept: application/json" -d "$i" https://"$DEST_DTR_URL"/enzi/v0/accounts?refresh_token="$DEST_DTR_TOKEN" 159 | done 160 | } 161 | 162 | 163 | 164 | 165 | putRepos() { 166 | cat orgList | sort -u | while IFS= read -r i; 167 | do 168 | cat ./$i/repoConfig | jq -c '.' | while IFS= read -r j; 169 | do 170 | curl --insecure -X POST --header "Content-Type: application/json" \ 171 | --header "Accept: application/json" -d "$j" https://"$DEST_DTR_URL"/api/v0/repositories/${i}?refresh_token="$DEST_DTR_TOKEN" 172 | done 173 | done 174 | } 175 | 176 | 177 | 178 | putTeams() { 179 | cat orgList | sort -u | while IFS= read -r i; 180 | do 181 | curl -s --insecure \ 182 | https://"$SRC_DTR_URL"/enzi/v0/accounts/$i/teams?refresh_token="$SRC_DTR_TOKEN" | jq -c '.teams[] | {name: .name, description: .description}' > ./$i/teamConfig 183 | 184 | cat ./$i/teamConfig | while IFS= read -r j; 185 | do 186 | curl --insecure -X POST --header "Content-Type: application/json" \ 187 | --header "Accept: application/json" -d "$j" https://"$DEST_DTR_URL"/enzi/v0/accounts/${i}/teams?refresh_token="$DEST_DTR_TOKEN" 188 | done 189 | done 190 | 191 | } 192 | 193 | putTeamMembers() { 194 | #Responds with 200 even though team members already exist (I guess this is because of PUT) 195 | cat orgList | sort -u | while IFS= read -r i; 196 | do 197 | cat ./$i/teamConfig | jq -r '.name' | while IFS= read -r j; 198 | do 199 | cat ./$i/$j/members | while IFS= read -r k; 200 | do 201 | teamMemberName=$(echo $k | jq -c -r .name) 202 | curl --insecure -X PUT --header "Content-Type: application/json" \ 203 | --header "Accept: application/json" -d "$k" https://"$DEST_DTR_URL"/enzi/v0/accounts/${i}/teams/${j}/members/${teamMemberName}?refresh_token="$DEST_DTR_TOKEN" 204 | done 205 | done 206 | done 207 | } 208 | 209 | ## Needs to be finished 210 | putTeamRepoAccess() { 211 | echo "putTeamRepoAccess" 212 | } 213 | 214 | ########################### 215 | # PUSH IMAGES # 216 | ########################### 217 | 218 | migrateImages() { 219 | echo "Image sync initiating" 220 | 221 | cat orgList | sort -u | while IFS= read -r i; 222 | do 223 | echo "Migrating images for $i" 224 | cat ./$i/repoConfig | jq -c -r '.name' | while IFS= read -r j; 225 | do 226 | TAGS=$(curl -s --insecure \ 227 | https://"$SRC_DTR_URL"/api/v0/repositories/${i}/${j}/tags?refresh_token="$SRC_DTR_TOKEN" | jq -c -r '.[].name') 228 | for k in $TAGS; 229 | do 230 | echo "Pulling $SRC_DTR_URL/$i/$j:$k" 231 | docker pull "$SRC_DTR_URL/$i/$j:$k" 232 | docker tag "$SRC_DTR_URL/$i/$j:$k" "$DEST_DTR_URL/$i/$j:$k" 233 | 234 | echo "Pushing $DEST_DTR_URL/$i/$j:$k" 235 | docker push "$DEST_DTR_URL/$i/$j:$k" 236 | done 237 | #Clean up images after each repo 238 | #echo "Repo $i complete. Pruning local images." 239 | #docker image prune -af 240 | done 241 | #Clean up images after each Org 242 | echo "Org $i complete. Pruning local images." 243 | docker image prune -af 244 | done 245 | } 246 | 247 | 248 | 249 | 250 | printAccessMap() { 251 | echo "Printing Team and Repo Access" 252 | cat orgList | sort -u | while IFS= read -r i; 253 | do 254 | echo "$i" 255 | echo "-------------------------" 256 | cat ./$i/teamConfig | jq -r '.name' | while IFS= read -r j; 257 | do 258 | echo " $j Members" 259 | cat ./$i/$j/members | while IFS= read -r member; 260 | do 261 | if [ $(echo $member | jq '.isAdmin') == 'true' ] 262 | then 263 | access="Admin" 264 | else 265 | access="Member" 266 | fi 267 | 268 | echo " " $(echo $member | jq -r .name) "-" "$access" 269 | done 270 | 271 | 272 | echo " $j Repository Access" 273 | cat ./$i/$j/repoAccess | while IFS= read -r access; 274 | do 275 | repoName=$(echo $access | jq -r '.repository.name') 276 | accessLevel=$(echo $access | jq -r '.accessLevel') 277 | echo " $i/$repoName - $accessLevel" 278 | done 279 | echo "" 280 | done 281 | echo "" 282 | done 283 | } 284 | 285 | 286 | usage() { 287 | echo "" 288 | echo "Usage: dtrctl -c [confguration file] COMMAND" 289 | echo "Pronounced: dtr-control" 290 | echo "" 291 | echo "Options" 292 | echo "" 293 | echo "-s, --source-metadata Pull org, repo, team, and team access metadata from source DTR and store locally" 294 | echo "-p, --push-metadata Push org, repo, team, and team access metadata from local source to dest DTR" 295 | echo "-i, --image-sync Pull images from source DTR and push to dest DTR" 296 | echo "-a, --print-access Print mapping of access rights between teams and repos" 297 | echo "--help Print usage" 298 | echo "" 299 | } 300 | 301 | 302 | 303 | ## Parse arguments 304 | while [[ $# -gt 0 ]] 305 | do 306 | case "$1" in 307 | -p|--push-metadata) 308 | PUSH=1 309 | shift 1 310 | ;; 311 | 312 | -i|--image-sync) 313 | SYNC_IMAGES=1 314 | shift 1 315 | ;; 316 | 317 | -a|--print-access) 318 | PRINT_ACCESS=1 319 | shift 1 320 | ;; 321 | 322 | -s|--source-metadata) 323 | PULL=1 324 | shift 1 325 | ;; 326 | 327 | -h|--help) 328 | usage 329 | exit 1 330 | ;; 331 | 332 | *) 333 | echo "Unknown argument: $1" 334 | usage 335 | exit 1 336 | ;; 337 | 338 | esac 339 | done 340 | 341 | #Entrypoint for program 342 | main 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | --------------------------------------------------------------------------------