You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by jb...@apache.org on 2018/08/01 22:41:28 UTC

[geode] branch develop updated: GEODE-5496: Refactor reaper using resources to reduce noise. (#2245)

This is an automated email from the ASF dual-hosted git repository.

jbarrett pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 7ccb3b8  GEODE-5496: Refactor reaper using resources to reduce noise. (#2245)
7ccb3b8 is described below

commit 7ccb3b8371260713d42d9b7211b087f01b1e17be
Author: Jacob Barrett <jb...@pivotal.io>
AuthorDate: Wed Aug 1 15:41:24 2018 -0700

    GEODE-5496: Refactor reaper using resources to reduce noise. (#2245)
---
 ci/pipelines/README.md                             |   5 +
 ci/pipelines/images/images.yml                     |  35 ++++++-
 ci/pipelines/meta.yml                              |  26 +++++-
 ci/pipelines/reaper.yml                            | 104 ++++++++++-----------
 .../concourse-metadata-resource/Dockerfile         |   0
 .../concourse-metadata-resource/files/check        |   0
 .../concourse-metadata-resource/files/in           |   0
 .../concourse-metadata-resource/files/out          |   0
 .../gce-instances-resource/Dockerfile              |  21 +++++
 ci/resource-types/gce-instances-resource/README.md |  67 +++++++++++++
 ci/resource-types/gce-instances-resource/build     |  21 +++++
 ci/resource-types/gce-instances-resource/check     |  45 +++++++++
 ci/resource-types/gce-instances-resource/in        |  73 +++++++++++++++
 ci/resource-types/gce-instances-resource/out       |  20 ++++
 .../gce-instances-resource/test/check.sh           |  47 ++++++++++
 .../gce-instances-resource/test/docker-compose.yml |  48 ++++++++++
 .../gce-instances-resource/test/in.sh              |  33 +++++++
 .../gce-instances-resource/test/out.sh             |  31 ++++++
 .../gce-instances-resource/test/pipeline.yml       |  82 ++++++++++++++++
 19 files changed, 600 insertions(+), 58 deletions(-)

diff --git a/ci/pipelines/README.md b/ci/pipelines/README.md
new file mode 100644
index 0000000..773b5a8
--- /dev/null
+++ b/ci/pipelines/README.md
@@ -0,0 +1,5 @@
+# Deploying Pipelines
+
+```bash
+./deploy_meta.sh <github account name>
+```
\ No newline at end of file
diff --git a/ci/pipelines/images/images.yml b/ci/pipelines/images/images.yml
index 0864038..e710d4e 100644
--- a/ci/pipelines/images/images.yml
+++ b/ci/pipelines/images/images.yml
@@ -97,7 +97,7 @@ resources:
     uri: https://github.com/((!geode-fork))/geode.git
     branch: ((!geode-build-branch))
     paths:
-    - ci/images/concourse-metadata-resource/*
+    - ci/resource-types/concourse-metadata-resource/*
 
 - name: concourse-metadata-resource-docker-image
   type: docker-image
@@ -121,6 +121,21 @@ resources:
     password: ((!docker-password))
     repository: gcr.io/apachegeode-ci/((!docker-image-prefix))metric-tools
 
+- name: gce-instances-resource-dockerfile
+  type: git
+  source:
+    uri: https://github.com/((!geode-fork))/geode.git
+    branch: ((!geode-build-branch))
+    paths:
+    - ci/resource-types/gce-instances-resource/*
+
+- name: gce-instances-resource-docker-image
+  type: docker-image
+  source:
+    username: ((!docker-username))
+    password: ((!docker-password))
+    repository: gcr.io/apachegeode-ci/((!docker-image-prefix))gce-instances-resource
+
 jobs:
 # apachegeode-build-concourse
 - name: build-apachegeode-build-concourse-docker-image
@@ -182,7 +197,7 @@ jobs:
   serial: true
   plan:
   - aggregate:
-    - get: alpine37-docker-image
+    - get: openjdk8-docker-image
       trigger: true
     - get: test-container-dockerfile
       trigger: true
@@ -202,7 +217,7 @@ jobs:
       trigger: true
   - put: concourse-metadata-resource-docker-image
     params:
-      build: concourse-metadata-resource-dockerfile/ci/images/concourse-metadata-resource
+      build: concourse-metadata-resource-dockerfile/ci/resource-types/concourse-metadata-resource
       tag_as_latest: true
 
 - name: build-metric-tools-docker-image
@@ -218,3 +233,17 @@ jobs:
     params:
       build: metric-tools-dockerfile/ci/images/metric-tools
       tag_as_latest: true
+
+- name: build-gce-instances-resource-docker-image
+  public: ((!public-pipelines))
+  serial: true
+  plan:
+  - aggregate:
+    - get: alpine-docker-image
+      trigger: true
+    - get: gce-instances-resource-dockerfile
+      trigger: true
+  - put: gce-instances-resource-docker-image
+    params:
+      build: gce-instances-resource-dockerfile/ci/resource-types/gce-instances-resource
+      tag_as_latest: true
diff --git a/ci/pipelines/meta.yml b/ci/pipelines/meta.yml
index 4b7e301..3c37cf5 100644
--- a/ci/pipelines/meta.yml
+++ b/ci/pipelines/meta.yml
@@ -161,7 +161,7 @@ jobs:
   - put: apachegeode-concourse
     params:
       pipelines:
-      - name: ((!geode-build-branch))-metrics
+      - name: ((!pipeline-prefix))metrics
         team: ((!concourse-team))
         config_file: geode-metrics-pipeline/ci/pipelines/metrics.yml
 
@@ -171,9 +171,29 @@ jobs:
   plan:
   - get: geode-reaper-pipeline
     trigger: true
+  - task: pipeline-vars
+    config:
+      outputs:
+      - name: pipeline-vars
+      platform: linux
+      image_resource:
+        type: docker-image
+        source:
+          repository: alpine
+      run:
+        path: /bin/sh
+        args:
+          - -c
+          - |
+            cat <<EOF >>pipeline-vars/vars
+            ---
+            pipeline-prefix: "((!pipeline-prefix))"
+            docker-image-prefix: "((!pipeline-prefix))"
   - put: apachegeode-concourse
     params:
       pipelines:
-      - name: ((!geode-build-branch))-reaper
+      - name: ((!pipeline-prefix))reaper
         team: ((!concourse-team))
-        config_file: geode-reaper-pipeline/ci/pipelines/reaper.yml
\ No newline at end of file
+        config_file: geode-reaper-pipeline/ci/pipelines/reaper.yml
+        vars_files:
+        - pipeline-vars/vars
diff --git a/ci/pipelines/reaper.yml b/ci/pipelines/reaper.yml
index 275149b..1cfa318 100644
--- a/ci/pipelines/reaper.yml
+++ b/ci/pipelines/reaper.yml
@@ -14,70 +14,70 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+
 ---
-groups: []
+
+resource_types:
+- name: gce-instances
+  type: docker-image
+  source:
+    username: ((!docker-username))
+    password: ((!docker-password))
+    repository: gcr.io/apachegeode-ci/((!docker-image-prefix))gce-instances-resource
+    tag: latest
+
 resources:
-- name: 1m
-  type: time
+- name: stopable-instance
+  type: gce-instances
   source:
-    interval: 1m
-resource_types: []
+    filter: 'labels.time-to-live:* AND labels.time-to-live<$(date +%s) AND status:RUNNING'
+
+- name: deletable-instance
+  type: gce-instances
+  source:
+    filter: 'labels.time-to-live:* AND labels.time-to-live<$(($(date +%s) - 86400)) AND status:TERMINATED'
+
 jobs:
-- name: reaper
-  serial: true
+- name: stop-instance
   plan:
-  - get: 1m
+  - get: stopable-instance
+    version: every
     trigger: true
-  - task: reap
+  - task: stop-instances
     config:
       platform: linux
       image_resource:
         type: docker-image
         source:
-          password: ((!docker-password))
-          repository: gcr.io/apachegeode-ci/alpine-tools
-          tag: latest
-          username: ((!docker-username))
+          repository: google/cloud-sdk
+          tag: alpine
+      inputs:
+      - name: stopable-instance
       run:
-        path: bash
+        path: /bin/sh
         args:
-        - -exc
+        - -ec
         - |
-          set -x
-          set -e
-
-          now=$(date +%s)
-
-          # Stop running instances that are expired
-
-          instances=$(gcloud compute instances list \
-              --filter="labels.time-to-live:* AND labels.time-to-live<${now} AND status:RUNNING" \
-              --format='json(name, zone)')
-
-          zone_names=$(echo "${instances}" | jq -r 'unique_by(.zone) | .[].zone')
-
-          for zone in ${zone_names}; do
-            zoneInstances=$(echo "${instances}" | jq -r --arg ZONE "${zone}" '.[] | select(.zone == $ZONE) | .name')
-            gcloud compute instances stop ${zoneInstances} --zone=${zone} &
-          done
-
-          wait
-
-          # Delete stopped instances over 24h expired
-
-          oneDay=$((60 * 60 * 24))
-          yesterday=$((${now} - ${oneDay}))
-
-          instances=$(gcloud compute instances list \
-              --filter="labels.time-to-live:* AND labels.time-to-live<${yesterday} AND status:TERMINATED" \
-              --format='json(name, zone:sort=1)')
-
-          zone_names=$(echo "${instances}" | jq -r 'unique_by(.zone) | .[].zone')
-
-          for zone in ${zone_names}; do
-            zoneInstances=$(echo "${instances}" | jq -r --arg ZONE "${zone}" '.[] | select(.zone == $ZONE) | .name')
-            gcloud compute instances delete ${zoneInstances} --zone=${zone} --quiet &
-          done
-
-          wait
+          gcloud compute instances stop $(cat stopable-instance/selfLink) --quiet
 
+- name: delete-instance
+  plan:
+  - get: deletable-instance
+    version: every
+    trigger: true
+  - task: delete-instance
+    config:
+      platform: linux
+      image_resource:
+        type: docker-image
+        source:
+          repository: google/cloud-sdk
+          tag: alpine
+      inputs:
+      - name: deletable-instance
+      run:
+        path: /bin/sh
+        args:
+        - -ec
+        - |
+          gcloud compute instances delete $(cat deletable-instance/selfLink) --quiet
diff --git a/ci/images/concourse-metadata-resource/Dockerfile b/ci/resource-types/concourse-metadata-resource/Dockerfile
similarity index 100%
rename from ci/images/concourse-metadata-resource/Dockerfile
rename to ci/resource-types/concourse-metadata-resource/Dockerfile
diff --git a/ci/images/concourse-metadata-resource/files/check b/ci/resource-types/concourse-metadata-resource/files/check
similarity index 100%
rename from ci/images/concourse-metadata-resource/files/check
rename to ci/resource-types/concourse-metadata-resource/files/check
diff --git a/ci/images/concourse-metadata-resource/files/in b/ci/resource-types/concourse-metadata-resource/files/in
similarity index 100%
rename from ci/images/concourse-metadata-resource/files/in
rename to ci/resource-types/concourse-metadata-resource/files/in
diff --git a/ci/images/concourse-metadata-resource/files/out b/ci/resource-types/concourse-metadata-resource/files/out
similarity index 100%
rename from ci/images/concourse-metadata-resource/files/out
rename to ci/resource-types/concourse-metadata-resource/files/out
diff --git a/ci/resource-types/gce-instances-resource/Dockerfile b/ci/resource-types/gce-instances-resource/Dockerfile
new file mode 100644
index 0000000..30df03c
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/Dockerfile
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM google/cloud-sdk:alpine
+
+RUN apk add --no-cache jq
+COPY check in out /opt/resource/
diff --git a/ci/resource-types/gce-instances-resource/README.md b/ci/resource-types/gce-instances-resource/README.md
new file mode 100644
index 0000000..a0385ae
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/README.md
@@ -0,0 +1,67 @@
+# Google Cloud Compute Engine Listing Concourse Resource Type
+
+Lists Google Cloud compute instances.
+## Resource Type Configuration
+```
+resource_types:
+- name: gce-instances
+  type: docker-image
+  source:
+    repository: gce-instances-resource
+    tag: latest
+```
+
+## Source Configuration
+- `filter`: (optional) A `bash` evaluated filter applied to listing instances.
+
+## `check`: Emits instances as versions
+If `filter` is provided in source configuration then the list of instances is filtered. Should be 
+combined with with a get task configured with `version: every` so that all instances found by check
+will be processed. 
+
+## `in`: Creates or destroys a instance
+Produces the following files with values from an instance found by `check`.
+- `id`: Contains the instance id.
+- `name`: Contains the instance name.
+- `selfLink`: Contains the instance selfLink URI.
+- `description`: Contains the description of instance in JSON format. 
+
+## Examples ##
+Stop all instances that are running and have a `time-to-live` label value older than now.
+```
+resource_types:
+- name: gce-instances
+  type: docker-image
+  source:
+    repository: gce-instances-resource
+    tag: latest
+
+resources:
+- name: stopable-instance
+  type: gce-instances
+  source:
+    filter: 'labels.time-to-live:* AND labels.time-to-live<$(date +%s) AND status:RUNNING'
+
+jobs:
+- name: stop-instance
+  plan:
+  - get: stopable-instance
+    version: every
+    trigger: true
+  - task: stop-instances
+    config:
+      platform: linux
+      image_resource:
+        type: docker-image
+        source:
+          repository: google/cloud-sdk
+          tag: alpine
+      inputs:
+      - name: stopable-instance
+      run:
+        path: /bin/sh
+        args:
+        - -exc
+        - |
+          gcloud compute instances stop $(cat stopable-instance/selfLink) --quiet
+```
\ No newline at end of file
diff --git a/ci/resource-types/gce-instances-resource/build b/ci/resource-types/gce-instances-resource/build
new file mode 100755
index 0000000..f97e1f8
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/build
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+docker build --no-cache -t gce-instances-resource .
+docker tag gce-instances-resource localhost:5000/gce-instances-resource
+docker push localhost:5000/gce-instances-resource
diff --git a/ci/resource-types/gce-instances-resource/check b/ci/resource-types/gce-instances-resource/check
new file mode 100755
index 0000000..0c3a5e9
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/check
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -x
+set -e
+
+BUILD_DIR=$1
+REQUEST=$(cat <&0)
+
+echo "BUILD_DIR: ${BUILD_DIR}" >&2
+echo "REQUEST: ${REQUEST}" >&2
+
+filter=$(echo "${REQUEST}" | jq -r '.source.filter // empty')
+
+instances=$(gcloud compute instances list \
+              --filter="$(eval echo \"${filter}\")" \
+              --format='json(id)')
+
+VERSIONS="${instances}"
+
+if [ -z "${VERSIONS}" ]; then
+  RESPONSE='[]'
+else
+  RESPONSE="${VERSIONS}"
+fi
+
+echo "RESPONSE ${RESPONSE}" >&2
+echo "${RESPONSE}"
+
+exit 0
diff --git a/ci/resource-types/gce-instances-resource/in b/ci/resource-types/gce-instances-resource/in
new file mode 100755
index 0000000..deba0c6
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/in
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -x
+set -e
+
+BUILD_DIR=$1
+REQUEST=$(cat <&0)
+
+echo "BUILD_DIR: ${BUILD_DIR}" >&2
+echo "REQUEST: ${REQUEST}" >&2
+
+VERSION=$(echo "${REQUEST}" | jq -r '.version.id // empty')
+
+if [ -z "${VERSION}" ]; then
+  RESPONSE=$(jq -n "{
+    version: {}
+  }")
+else
+  id="${VERSION}"
+  echo "${id}" > ${BUILD_DIR}/id
+
+  COMMAND="gcloud compute instances list --filter="id:${id}" --format=json"
+
+  echo "COMMAND: ${COMMAND}" >&2
+  RESULT=$(eval "${COMMAND}" | jq '.[0]')
+  echo "RESULT: ${RESULT}" >&2
+
+  description="${RESULT}"
+  echo "${description}" > ${BUILD_DIR}/description
+
+  name=$(jq -r '.name' ${BUILD_DIR}/description)
+  echo "${name}" > ${BUILD_DIR}/name
+
+  selfLink=$(jq -r '.selfLink' ${BUILD_DIR}/description)
+  echo "${selfLink}" > ${BUILD_DIR}/selfLink
+
+  cat ${BUILD_DIR}/id >&2
+  cat ${BUILD_DIR}/description >&2
+
+  RESPONSE=$(jq -n "{
+    version: {
+      id: $(echo ${VERSION} | jq -R .)
+    },
+    metadata: [{
+      name: \"name\",
+      value: $(echo ${name} | jq -R .)
+    },{
+      name: \"selfLink\",
+      value: $(echo ${selfLink} | jq -R .)
+    }]
+  }")
+fi
+
+echo "RESPONSE ${RESPONSE}" >&2
+echo "${RESPONSE}"
+
+exit 0
diff --git a/ci/resource-types/gce-instances-resource/out b/ci/resource-types/gce-instances-resource/out
new file mode 100755
index 0000000..ca8f37b
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/out
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+echo NOT IMPLEMENTED >&2
+exit 1
diff --git a/ci/resource-types/gce-instances-resource/test/check.sh b/ci/resource-types/gce-instances-resource/test/check.sh
new file mode 100755
index 0000000..db60f70
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/test/check.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+jq -n '
+{
+  "version": {},
+  "source": {
+    "filter": "labels.time-to-live:* AND labels.time-to-live<$(date +%s) AND status:RUNNING"
+  }
+}
+' | \
+    docker run -i --rm \
+        --env BUILD_TEAM_NAME=Team \
+        --env BUILD_PIPELINE_NAME=Pipeline \
+        --env BUILD_JOB_NAME=Job \
+        --env BUILD_NAME=1 \
+        gce-instances-resource /opt/resource/check /tmp
+
+jq -n '
+{
+  "version": {},
+  "source": {
+    "filter": "labels.time-to-live:* AND labels.time-to-live<$(($(date +%s) - 86400)) AND status:TERMINATED"
+  }
+}
+' | \
+    docker run -i --rm \
+        --env BUILD_TEAM_NAME=Team \
+        --env BUILD_PIPELINE_NAME=Pipeline \
+        --env BUILD_JOB_NAME=Job \
+        --env BUILD_NAME=1 \
+        gce-instances-resource /opt/resource/check /tmp
diff --git a/ci/resource-types/gce-instances-resource/test/docker-compose.yml b/ci/resource-types/gce-instances-resource/test/docker-compose.yml
new file mode 100644
index 0000000..6bf36e6
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/test/docker-compose.yml
@@ -0,0 +1,48 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+version: '3'
+
+services:
+  concourse-db:
+    image: postgres
+    environment:
+    - POSTGRES_DB=concourse
+    - POSTGRES_PASSWORD=concourse_pass
+    - POSTGRES_USER=concourse_user
+    - PGDATA=/database
+
+  registry:
+    image: registry
+    ports: ["5000:5000"]
+
+  concourse:
+    image: concourse/concourse
+    command: quickstart
+    privileged: true
+    depends_on: [concourse-db, registry]
+    ports: ["8080:8080"]
+    environment:
+    - CONCOURSE_POSTGRES_HOST=concourse-db
+    - CONCOURSE_POSTGRES_USER=concourse_user
+    - CONCOURSE_POSTGRES_PASSWORD=concourse_pass
+    - CONCOURSE_POSTGRES_DATABASE=concourse
+    - CONCOURSE_EXTERNAL_URL
+    - CONCOURSE_ADD_LOCAL_USER=test:$$2a$$10$$0W9/ilCpYXY/yCPpaOD.6eCrGda/fnH3D4lhsw1Mze0WTID5BuiTW
+    - CONCOURSE_MAIN_TEAM_ALLOW_ALL_USERS=true
+    - CONCOURSE_WORKER_GARDEN_NETWORK
+    - CONCOURSE_NO_REALLY_I_DONT_WANT_ANY_AUTH=true
diff --git a/ci/resource-types/gce-instances-resource/test/in.sh b/ci/resource-types/gce-instances-resource/test/in.sh
new file mode 100755
index 0000000..b0a4f96
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/test/in.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+jq -n '
+{
+  "version": {
+    "id": "1929725350891113587"
+  },
+  "source": {
+  }
+}
+' | \
+    docker run -i --rm \
+        --env BUILD_TEAM_NAME=Team \
+        --env BUILD_PIPELINE_NAME=Pipeline \
+        --env BUILD_JOB_NAME=Job \
+        --env BUILD_NAME=1 \
+        gce-instances-resource /opt/resource/in /tmp
diff --git a/ci/resource-types/gce-instances-resource/test/out.sh b/ci/resource-types/gce-instances-resource/test/out.sh
new file mode 100755
index 0000000..d5e55d1
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/test/out.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+jq -n '
+{
+  "params": {}
+  },
+  "source": {}
+}
+' | \
+    docker run -i --rm \
+        --env BUILD_TEAM_NAME=Team \
+        --env BUILD_PIPELINE_NAME=Pipeline \
+        --env BUILD_JOB_NAME=Job \
+        --env BUILD_NAME=1 \
+        gce-instances-resource /opt/resource/out /tmp
diff --git a/ci/resource-types/gce-instances-resource/test/pipeline.yml b/ci/resource-types/gce-instances-resource/test/pipeline.yml
new file mode 100644
index 0000000..de5d2d0
--- /dev/null
+++ b/ci/resource-types/gce-instances-resource/test/pipeline.yml
@@ -0,0 +1,82 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+---
+resource_types:
+- name: gce-instances
+  type: docker-image
+  source:
+    repository: registry:5000/gce-instances-resource
+    tag: latest
+    insecure_registries:
+    - "registry:5000"
+
+resources:
+- name: stopable-instance
+  type: gce-instances
+  source:
+    filter: 'labels.time-to-live:* AND labels.time-to-live<$(date +%s) AND status:RUNNING'
+
+- name: destroyable-instance
+  type: gce-instances
+  source:
+    filter: 'labels.time-to-live:* AND labels.time-to-live<$(($(date +%s) - 86400)) AND status:TERMINATED'
+
+jobs:
+- name: stop-instance
+  plan:
+  - get: stopable-instance
+    version: every
+    trigger: true
+  - task: stop-instances
+    config:
+      platform: linux
+      image_resource:
+        type: docker-image
+        source:
+          repository: google/cloud-sdk
+          tag: alpine
+      inputs:
+      - name: stopable-instance
+      run:
+        path: /bin/sh
+        args:
+        - -exc
+        - |
+          gcloud compute instances stop $(cat stopable-instance/selfLink) --quiet
+
+- name: destroy-instance
+  plan:
+  - get: destroyable-instance
+    version: every
+    trigger: true
+  - task: reap-destroyable
+    config:
+      platform: linux
+      image_resource:
+        type: docker-image
+        source:
+          repository: google/cloud-sdk
+          tag: alpine
+      inputs:
+      - name: destroyable-instance
+      run:
+        path: /bin/sh
+        args:
+        - -exc
+        - |
+          gcloud compute instances destroy $(cat destroyable-instance/selfLink) --quiet