Add a dev env in Okteto (#128)

* add a dev env
This commit is contained in:
Salah Al Saleh
2024-02-05 09:16:45 -08:00
committed by GitHub
parent ccfdafc048
commit 7624fbfa13
20 changed files with 424 additions and 24 deletions

View File

@@ -1,3 +1,5 @@
[![Develop on Okteto](https://okteto.com/develop-okteto.svg)](https://replicated.okteto.dev/deploy?repository=https://github.com/replicatedhq/ttl.sh&branch=main)
# ttl.sh
## An ephemeral container registry for CI workflows.
@@ -6,3 +8,37 @@
ttl.sh is an anonymous, expiring Docker container registry using the official Docker Registry image. This is a set of tools and configurations that can be used to deploy the registry without authentication, but with self-expiring images.
# Development
Development for the services in this project is done through [Okteto](https://replicated.okteto.dev).
## Setup
1. Install the Okteto CLI (`brew install okteto`)
2. Setup Okteto CLI (`okteto context use https://replicated.okteto.dev`)
3. Setup Okteto context in kubectl (`okteto context update-kubeconfig`)
4. Deploy your current branch. (from the ttl.sh root directory: `okteto pipeline deploy`)
## Debugging
Okteto is utilized for debugging. New build targets have been added to allow building and running each service in debug mode.
1. Replace the default container in your Okteto environment with a development container.
1. From the root directory: `okteto up` or `okteto up <service name>`
2. Run the build targets for the desired service:
1. ttl-hooks: `make deps build hooks`
2. ttl-reaper: `make deps build reap`
3. Stop development and go back to the default container.
1. From the root directory: `okteto down` or `okteto down <service name>`
## Example workflows
### Switching branches or rebasing
1. `git checkout my-new-branch`
2. `okteto pipeline deploy`
3. (make code changes)
4. `okteto up`
5. (test changes, find they don't work, make more changes)...
6. `okteto down`
7. (commit code, and be happy)

View File

@@ -1,5 +1,3 @@
import { tryParsePutTagRequest, handleTagManifestRequest } from "./tag_manifest";
if (typeof addEventListener === 'function') {
addEventListener('fetch', (e: Event): void => {
// work around as strict typescript check doesn't allow e to be of type FetchEvent

56
hooks/.stignore Normal file
View File

@@ -0,0 +1,56 @@
.git
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless

View File

@@ -1,4 +1,5 @@
FROM node:10 as deps
ADD ./package.json /src/package.json
ADD ./Makefile /src/Makefile
ADD . /src

View File

@@ -1,4 +1,5 @@
FROM node:10 as deps
ADD ./package.json /src/package.json
ADD ./Makefile /src/Makefile
ADD . /src

View File

@@ -11,7 +11,7 @@ deps:
.PHONY: lint
lint:
`npm bin`/tslint --project ./tsconfig.json --fix
npx tslint --project ./tsconfig.json --fix
.PHONY: test
test: build
@@ -19,10 +19,10 @@ test: build
.PHONY: build
build: prebuild
`npm bin`/tsc
npx tsc
.PHONY: run
run:
.PHONY: hooks
hooks:
node --no-deprecation ./build/server.js hooks
.PHONY: reap

View File

@@ -1,7 +1,6 @@
import * as util from "util";
import { Server } from "../server/server";
import { logger } from "../logger";
import { param } from "../util";
exports.name = "hooks";
exports.describe = "Start and run the hook api server";
@@ -17,7 +16,7 @@ exports.handler = async (argv) => {
};
async function main(argv): Promise<any> {
process.on('SIGTERM', function onSigterm () {
process.on("SIGTERM", function onSigterm() {
logger.info(`Got SIGTERM, cleaning up`);
process.exit();
});

View File

@@ -5,6 +5,7 @@ import * as redis from "redis";
import { promisify } from "util";
import * as rp from "request-promise";
const registryUrl = process.env["REGISTRY_URL"] || "https://ttl.sh";
const client = redis.createClient({url: process.env["REDISCLOUD_URL"]});
const smembersAsync = promisify(client.smembers).bind(client);
const sremAsync = promisify(client.srem).bind(client);
@@ -25,7 +26,7 @@ exports.handler = async (argv) => {
};
async function main(argv): Promise<any> {
process.on('SIGTERM', function onSigterm () {
process.on("SIGTERM", function onSigterm() {
logger.info(`Got SIGTERM, cleaning up`);
process.exit();
});
@@ -73,17 +74,17 @@ async function reapExpiredImages() {
const imageAndTag = image.split(":");
const headers = {
"Accept": "application/vnd.docker.distribution.manifest.v2+json",
Accept: "application/vnd.docker.distribution.manifest.v2+json",
};
// Get the manifest from the tag
const getOptions = {
method: "HEAD",
uri: `https://ttl.sh/v2/${imageAndTag[0]}/manifests/${imageAndTag[1]}`,
uri: `${registryUrl}/v2/${imageAndTag[0]}/manifests/${imageAndTag[1]}`,
headers,
resolveWithFullResponse: true,
simple: false,
}
};
const getResponse = await rp(getOptions);
if (getResponse.statusCode == 404) {
@@ -92,7 +93,7 @@ async function reapExpiredImages() {
continue;
}
const deleteURI = `https://ttl.sh/v2/${imageAndTag[0]}/manifests/${getResponse.headers.etag.replace(/"/g,"")}`;
const deleteURI = `${registryUrl}/v2/${imageAndTag[0]}/manifests/${getResponse.headers.etag.replace(/"/g, "")}`;
// Remove from the registry
const options = {
@@ -101,7 +102,7 @@ async function reapExpiredImages() {
headers,
resolveWithFullResponse: true,
simple: false,
}
};
await rp(options);

View File

@@ -57,10 +57,10 @@ export class HookAPI {
console.log(`parsing tag ${tag}`);
let expiresIn = durationfromTag(tag);
if (expiresIn <= 0) {
expiresIn = defaultDuration
expiresIn = defaultDuration;
}
if (expiresIn > maxDuration) {
expiresIn = maxDuration
expiresIn = maxDuration;
}
await saddAsync("current.images", imageWithTag);

View File

@@ -13,7 +13,7 @@ function initLogger(): any {
logger.level = process.env["LOG_LEVEL"] || "warn";
return logger;
} else {
const logger = pino(logOptions)
const logger = pino(logOptions);
logger.level = process.env["LOG_LEVEL"] || "warn";
return logger;
}

View File

@@ -122,7 +122,7 @@ export const preRequest = (req: express.Request, reqId: string) => {
export const requestId = (req: express.Request, handlerName: string) => {
const id = `${handlerName}:${uuid.v4().replace("-", "").substring(0, 8)}`;
const clientID: string = <string> req.headers["x-request-uuid"];
const clientID: string = req.headers["x-request-uuid"] as string;
if (clientID) {
return `${id}.${clientID.substring(0, 8)}`;

View File

@@ -21,7 +21,7 @@ if (port == null || port == "") {
debug: false,
logger: {
level: "warn",
}
},
})
export class Server extends ServerLoader {

View File

@@ -0,0 +1,3 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources: []

View File

@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ttl-hooks
labels:
app: ttl-hooks
spec:
selector:
matchLabels:
app: ttl-hooks
template:
metadata:
labels:
app: ttl-hooks
spec:
restartPolicy: Always
initContainers:
- name: wait-for-registry
image: quay.io/curl/curl:latest
command:
- sh
- -c
- |
until curl -s -o /dev/null -w "%{http_code}" ttl-registry:5000/v2/; do
echo "Waiting for Docker Registry to be ready..."
sleep 2
done
- name: wait-for-redis
image: redis:latest
command:
- sh
- -c
- |
until redis-cli -h ttl-redis -p 6379 ping | grep "PONG"; do
echo "Waiting for Redis to be ready..."
sleep 2
done
containers:
- name: ttl-hooks
image: ttl-hooks
ports:
- containerPort: 8000
env:
- name: REDISCLOUD_URL
value: redis://ttl-redis:6379
- name: HOOK_TOKEN
value: dev-hook-token
---
apiVersion: v1
kind: Service
metadata:
name: ttl-hooks
labels:
app: ttl-hooks
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 8000
targetPort: 8000
selector:
app: ttl-hooks

View File

@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- ./hooks.yaml
- ./reaper.yaml
- ./redis.yaml
- ./registry.yaml

View File

@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ttl-reaper
labels:
app: ttl-reaper
spec:
selector:
matchLabels:
app: ttl-reaper
template:
metadata:
labels:
app: ttl-reaper
spec:
restartPolicy: Always
initContainers:
- name: wait-for-registry
image: quay.io/curl/curl:latest
command:
- sh
- -c
- |
until curl -s -o /dev/null -w "%{http_code}" ttl-registry:5000/v2/; do
echo "Waiting for Docker Registry to be ready..."
sleep 2
done
- name: wait-for-redis
image: redis:latest
command:
- sh
- -c
- |
until redis-cli -h ttl-redis -p 6379 ping | grep "PONG"; do
echo "Waiting for Redis to be ready..."
sleep 2
done
containers:
- name: ttl-reaper
image: ttl-reaper
ports:
- containerPort: 8000
env:
- name: REGISTRY_URL
value: http://ttl-registry:5000
- name: REDISCLOUD_URL
value: redis://ttl-redis:6379
---
apiVersion: v1
kind: Service
metadata:
name: ttl-reaper
labels:
app: ttl-reaper
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 8000
targetPort: 8000
selector:
app: ttl-reaper

View File

@@ -0,0 +1,50 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ttl-redis
labels:
app: ttl-redis
spec:
replicas: 1
selector:
matchLabels:
app: ttl-redis
template:
metadata:
labels:
app: ttl-redis
spec:
containers:
- name: redis
image: redis:latest
ports:
- containerPort: 6379
volumeMounts:
- name: redis-data
mountPath: /data
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 20
timeoutSeconds: 5
periodSeconds: 3
volumes:
- name: redis-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: ttl-redis
labels:
app: ttl-redis
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 6379
targetPort: 6379
selector:
app: ttl-redis

View File

@@ -0,0 +1,90 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ttl-registry
labels:
app: ttl-registry
spec:
replicas: 1
selector:
matchLabels:
app: ttl-registry
template:
metadata:
labels:
app: ttl-registry
spec:
containers:
- name: ttl-registry
image: registry:2
ports:
- containerPort: 5000
volumeMounts:
- name: registry-data
mountPath: /var/lib/registry
- name: registry-config
mountPath: /etc/docker/registry/config.yml
subPath: config.yml
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 1
successThreshold: 2
timeoutSeconds: 1
httpGet:
path: /
port: 5000
scheme: HTTP
volumes:
- name: registry-data
emptyDir: {}
- name: registry-config
configMap:
name: registry-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-config
labels:
app: ttl-registry
data:
config.yml: |
version: 0.1
log:
level: debug
storage:
delete:
enabled: true
filesystem:
rootdirectory: /var/lib/registry
http:
addr: 0.0.0.0:5000
headers:
X-Content-Type-Options: [nosniff]
notifications:
endpoints:
- name: ttl-hooks
url: http://ttl-hooks:8000/v1/hook/registry-event
headers:
Authorization: ["Token dev-hook-token"]
timeout: 200ms
threshold: 3
backoff: 5s
---
apiVersion: v1
kind: Service
metadata:
name: ttl-registry
labels:
app: ttl-registry
annotations:
dev.okteto.com/auto-ingress: "true"
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 5000
targetPort: 5000
selector:
app: ttl-registry

33
okteto.yml Normal file
View File

@@ -0,0 +1,33 @@
build:
ttl-hooks:
context: ./hooks
dockerfile: ./hooks/Dockerfile.hooks
ttl-reaper:
context: ./hooks
dockerfile: ./hooks/Dockerfile.reap
deploy:
- cd kustomize/overlays/dev && kustomize edit set image ttl-hooks=${OKTETO_BUILD_TTL_HOOKS_IMAGE}
- cd kustomize/overlays/dev && kustomize edit set image ttl-reaper=${OKTETO_BUILD_TTL_REAPER_IMAGE}
- kubectl apply -k kustomize/overlays/dev
dev:
ttl-hooks:
command: make deps build && bash || bash
workdir: /src
sync:
- ./hooks:/src
resources:
limits:
cpu: "1"
memory: 1Gi
ttl-reaper:
command: make deps build && bash || bash
workdir: /src
sync:
- ./hooks:/src
resources:
limits:
cpu: "1"
memory: 1Gi