build stuff

This commit is contained in:
Evan Reichard 2025-09-20 18:29:27 -04:00
parent d2b9f273e0
commit 516427dcda
8 changed files with 206 additions and 8 deletions

35
.drone.yml Normal file
View File

@ -0,0 +1,35 @@
kind: pipeline
type: docker
name: default
trigger:
branch:
- master
steps:
# Unit Tests
- name: tests
image: golang
commands:
- make tests
# Fetch tags
- name: fetch tags
image: alpine/git
commands:
- git fetch --tags
# Publish docker image
- name: publish docker
image: plugins/docker
settings:
repo: gitea.va.reichard.io/evan/conduit
registry: gitea.va.reichard.io
tags:
- dev
custom_dns:
- 8.8.8.8
username:
from_secret: docker_username
password:
from_secret: docker_password

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
cover.html

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
# Certificates & Timezones
FROM alpine AS alpine
RUN apk update && apk add --no-cache ca-certificates tzdata
# Build Image
FROM golang:1.24 AS build
# Create Package Directory
RUN mkdir -p /opt/conduit
# Copy Source
WORKDIR /src
COPY . .
# Compile
RUN go build \
-ldflags "-X reichard.io/conduit/config.version=`git describe --tags`" \
-o /opt/conduit/conduit
# Create Image
FROM busybox:1.36
COPY --from=alpine /etc/ssl/certs /etc/ssl/certs
COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=build /opt/conduit /opt/conduit
WORKDIR /opt/conduit
EXPOSE 8080
ENTRYPOINT ["/opt/conduit/conduit", "serve"]

29
Dockerfile-BuildKit Normal file
View File

@ -0,0 +1,29 @@
# Certificates & Timezones
FROM alpine AS alpine
RUN apk update && apk add --no-cache ca-certificates tzdata
# Build Image
FROM --platform=$BUILDPLATFORM golang:1.24 AS build
# Create Package Directory
WORKDIR /src
RUN mkdir -p /opt/conduit
# Cache Dependencies & Compile
ARG TARGETOS
ARG TARGETARCH
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
GOOS=$TARGETOS GOARCH=$TARGETARCH go build \
-ldflags "-X reichard.io/conduit/config.version=`git describe --tags`" \
-o /opt/conduit/conduit
# Create Image
FROM busybox:1.36
COPY --from=alpine /etc/ssl/certs /etc/ssl/certs
COPY --from=alpine /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=build /opt/conduit /opt/conduit
WORKDIR /opt/conduit
EXPOSE 8080
ENTRYPOINT ["/opt/conduit/conduit", "serve"]

35
Makefile Normal file
View File

@ -0,0 +1,35 @@
build_local:
go mod download
rm -r ./build || true
mkdir -p ./build
env GOOS=linux GOARCH=amd64 go build -ldflags "-X reichard.io/conduit/config.version=`git describe --tags`" -o ./build/server_linux_amd64
env GOOS=linux GOARCH=arm64 go build -ldflags "-X reichard.io/conduit/config.version=`git describe --tags`" -o ./build/server_linux_arm64
env GOOS=darwin GOARCH=arm64 go build -ldflags "-X reichard.io/conduit/config.version=`git describe --tags`" -o ./build/server_darwin_arm64
env GOOS=darwin GOARCH=amd64 go build -ldflags "-X reichard.io/conduit/config.version=`git describe --tags`" -o ./build/server_darwin_amd64
docker_build_local:
docker build -t conduit:latest .
docker_build_release_dev:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t gitea.va.reichard.io/evan/conduit:dev \
-f Dockerfile-BuildKit \
--push .
docker_build_release_latest:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t gitea.va.reichard.io/evan/conduit:latest \
-t gitea.va.reichard.io/evan/conduit:`git describe --tags` \
-f Dockerfile-BuildKit \
--push .
clean:
rm -rf ./build
tests:
SET_TEST=set_val go test -coverpkg=./... ./... -coverprofile=./cover.out
go tool cover -html=./cover.out -o ./cover.html
rm ./cover.out

39
cmd/tunnel.go Normal file
View File

@ -0,0 +1,39 @@
package cmd
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"reichard.io/conduit/client"
"reichard.io/conduit/config"
)
var tunnelCmd = &cobra.Command{
Use: "tunnel <name> <host:port>",
Short: "Create a conduit tunnel",
Run: func(cmd *cobra.Command, args []string) {
// Get Client Config
cfg, err := config.GetClientConfig(cmd.Flags())
if err != nil {
log.Fatal("failed to get client config:", err)
}
// Create Tunnel
tunnel, err := client.NewTunnel(cfg)
if err != nil {
log.Fatal("failed to create tunnel:", err)
}
// Start Tunnel
log.Infof("creating TCP tunnel: %s -> %s", cfg.TunnelName, cfg.TunnelTarget)
if err := tunnel.Start(); err != nil {
log.Fatal("failed to start tunnel:", err)
}
},
}
func init() {
configDefs := config.GetConfigDefs[config.ClientConfig]()
for _, d := range configDefs {
tunnelCmd.Flags().String(d.Key, d.Default, d.Description)
}
}

View File

@ -11,6 +11,8 @@ import (
"github.com/spf13/pflag"
)
var version string = "develop"
type ConfigDef struct {
Key string
Env string
@ -102,6 +104,10 @@ func GetConfigDefs[T ServerConfig | ClientConfig]() []ConfigDef {
return defs
}
func GetVersion() string {
return version
}
func getConfigValue(cmdFlags *pflag.FlagSet, def ConfigDef) string {
// 1. Get Flags First
if cmdFlags != nil {

View File

@ -3,6 +3,7 @@ package server
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
@ -19,6 +20,16 @@ import (
"reichard.io/conduit/types"
)
type InfoResponse struct {
Tunnels []TunnelInfo `json:"tunnels"`
Version string `json:"version"`
}
type TunnelInfo struct {
Name string `json:"name"`
Target string `json:"target"`
}
type TunnelConnection struct {
*websocket.Conn
name string
@ -76,16 +87,31 @@ func (s *Server) Start() error {
}
}
func (s *Server) getStatus(w http.ResponseWriter, _ *http.Request) {
func (s *Server) getInfo(w http.ResponseWriter, _ *http.Request) {
// Get Tunnels
var allTunnels []TunnelInfo
s.mu.RLock()
count := len(s.tunnels)
for t, c := range s.tunnels {
allTunnels = append(allTunnels, TunnelInfo{
Name: t,
Target: c.RemoteAddr().String(),
})
}
s.mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
// Create Response
d, err := json.MarshalIndent(InfoResponse{
Tunnels: allTunnels,
Version: config.GetVersion(),
}, "", " ")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
response := fmt.Sprintf(`{"tunnels": %d}`, count)
_, _ = w.Write([]byte(response))
// Send Response
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(d)
}
func (s *Server) proxyRawConnection(clientConn net.Conn, tunnelConn *TunnelConnection, dataReader io.Reader) {
@ -212,8 +238,8 @@ func (s *Server) handleAsHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/_conduit/tunnel":
s.createTunnel(w, r)
case "/_conduit/status":
s.getStatus(w, r)
case "/_conduit/info":
s.getInfo(w, r)
default:
w.WriteHeader(http.StatusNotFound)
}