diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..212109a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,29 @@ +default: + image: $CI_REGISTRY/imageroot/buildah:latest + +stages: + - build + +.container: + before_script: + - podman login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY <<<$CI_REGISTRY_PASSWORD + - podman login -u $NEXUS_USERNAME --password-stdin $NEXUS_DOCKER_IO <<<$NEXUS_PASSWORD + +build-master-container: + extends: .container + stage: build + script: + - podman build --build-arg DOCKER_IO=${NEXUS_DOCKER_IO} -t ${CI_REGISTRY_IMAGE}:master . + - podman push ${CI_REGISTRY_IMAGE}:master + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +build-tagged-container: + extends: .container + stage: build + script: + - podman build --build-arg DOCKER_IO=${NEXUS_DOCKER_IO} -t ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} -t ${CI_REGISTRY_IMAGE}:latest . + - podman push ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} + - podman push ${CI_REGISTRY_IMAGE}:latest + rules: + - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/ diff --git a/Dockerfile b/Dockerfile index 94a732a..ab06a02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,15 @@ -FROM golang:1.17-alpine AS build +ARG DOCKER_IO=docker.io + +FROM ${DOCKER_IO}/library/golang:1.24-alpine AS build WORKDIR /code COPY time.go /code RUN go build ./time.go -FROM alpine:latest AS final - -MAINTAINER Yehuda Deutsch +FROM ${DOCKER_IO}/library/alpine:latest AS final +RUN apk add --no-cache tzdata WORKDIR /code COPY --from=build /code/time /code/time diff --git a/time.go b/time.go index d8c755e..5b12274 100644 --- a/time.go +++ b/time.go @@ -1,25 +1,87 @@ package main import ( + "encoding/json" "fmt" "net/http" + "os" "time" ) -func getTime(location string) time.Time { - loc, _ := time.LoadLocation("UTC") - return time.Now().In(loc) +func getTime(location string) (time.Time, error) { + loc, err := time.LoadLocation(location) + if err != nil { + return time.Now(), err + } + return time.Now().In(loc), err +} + +var zoneDir string + +func loadZoneLocations(path string) []string { + var locations []string + zoneFiles, _ := os.ReadDir("/usr/share/zoneinfo/" + path) + for _, file := range zoneFiles { + if file.IsDir() { + locations = append(locations, loadZoneLocations(path+"/"+file.Name())...) + } else { + if _, err := time.LoadLocation((path + "/" + file.Name())[1:]); err != nil { + continue + } + locations = append(locations, (path + "/" + file.Name())[1:]) + } + } + return locations +} + +var runtimeLocations = loadZoneLocations(zoneDir) + +func mainHandler(w http.ResponseWriter, r *http.Request) { + now, err := getTime("UTC") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, "Could not get current time") + return + } + + fmt.Fprintf(w, now.Format("2006-01-02 15:04:05")) +} + +func dayInYearHandler(w http.ResponseWriter, r *http.Request) { + now, err := getTime("UTC") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, "Could not get current time") + return + } + fmt.Fprintf(w, "%s%d", string(now.Format("06")[1]), now.YearDay()) +} + +func timeAtZoneHandler(w http.ResponseWriter, r *http.Request) { + timezone := r.PathValue("timezone") + now, err := getTime(timezone) + if err != nil { + w.WriteHeader(http.StatusNotFound) + fmt.Fprint(w, "Timezone not found") + return + } + fmt.Fprintf(w, now.Format("2006-01-02 15:04:05")) +} + +func ZoneListHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(runtimeLocations) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, "Could not encode zones") + } } func main() { - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, getTime("UTC").Format("2006-01-02 15:04:05")) - }) - - http.HandleFunc("/diy", func(w http.ResponseWriter, r *http.Request) { - now := getTime("UTC") - fmt.Fprintf(w, "%s%d", string(now.Format("06")[1]), now.YearDay()) - }) + http.HandleFunc("GET /", mainHandler) + http.HandleFunc("GET /diy", dayInYearHandler) + http.HandleFunc("GET /at/{timezone...}", timeAtZoneHandler) + http.HandleFunc("GET /timezones", ZoneListHandler) listen := ":8000" fmt.Println("Listening on " + listen)