From ae362e28497e31ae5bcf10fc13bf93d10b3b0be8 Mon Sep 17 00:00:00 2001 From: Alex Lovell-Troy Date: Tue, 29 Oct 2024 15:50:40 -0600 Subject: [PATCH] Build Improvements * Add .goreleaser.yml * Update Dockerfile to use goreleaser * Use the version information more consistent with smd * Use coredhcp-generator without cloning it. --- .github/workflows/Realease.yaml | 76 +++++++++++++++++++ .gitignore | 6 ++ .goreleaser.yml | 97 ++++++++++++++++++++++++ Dockerfile | 71 ++++------------- LICENSE | 25 ++++++ README.md | 36 +++++++++ bootloop/main.go | 16 ++-- cmd/coredhcp.go | 130 ++++++++++++++++++++++++++++++++ coresmd/main.go | 8 +- generator/coredhcp.go.template | 104 +++++++++++++++++++++++++ generator/plugins.txt | 15 ++++ go.mod | 4 +- go.sum | 20 +++++ internal/version/version.go | 61 +++++++++++++-- 14 files changed, 593 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/Realease.yaml create mode 100644 .gitignore create mode 100644 .goreleaser.yml create mode 100644 LICENSE create mode 100644 cmd/coredhcp.go create mode 100644 generator/coredhcp.go.template create mode 100644 generator/plugins.txt diff --git a/.github/workflows/Realease.yaml b/.github/workflows/Realease.yaml new file mode 100644 index 0000000..61d3472 --- /dev/null +++ b/.github/workflows/Realease.yaml @@ -0,0 +1,76 @@ +name: Release with goreleaser + +on: + push: + tags: + - v* + +permissions: write-all # Necessary for the generate-build-provenance action with containers + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Set up latest stable Go + uses: actions/setup-go@v5 + with: + go-version: stable + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-tags: 1 + fetch-depth: 1 + + # Set environment variables required by GoReleaser + - name: Set build environment variables + run: | + echo "GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" >> $GITHUB_ENV + echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV + echo "GO_VERSION=$(go version | awk '{print $3}')" >> $GITHUB_ENV + echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV + echo "CGO_ENABLED=1" >> $GITHUB_ENV + - name: Docker Login + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Release with goreleaser + uses: goreleaser/goreleaser-action@v6 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + version: '~> v2' + args: release --clean + id: goreleaser + + - name: Process goreleaser output + id: process_goreleaser_output + run: | + echo "const fs = require('fs');" > process.js + echo 'const artifacts = ${{ steps.goreleaser.outputs.artifacts }}' >> process.js + echo "const firstNonNullDigest = artifacts.find(artifact => artifact.extra && artifact.extra.Digest != null)?.extra.Digest;" >> process.js + echo "console.log(firstNonNullDigest);" >> process.js + echo "fs.writeFileSync('digest.txt', firstNonNullDigest);" >> process.js + node process.js + echo "digest=$(cat digest.txt)" >> $GITHUB_OUTPUT + + - name: Attest coredhcp binary linux_amd64 + uses: actions/attest-build-provenance@v1 + with: + subject-path: dist/coredhcp_linux_amd64_v3/coredhcp + + - name: Attest coredhcp binary linux_arm64 + uses: actions/attest-build-provenance@v1 + with: + subject-path: dist/coredhcp_linux_arm64/coredhcp + + - name: generate build provenance of docker container + uses: actions/attest-build-provenance@v1 + with: + subject-name: ghcr.io/openchami/coredhcp + subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} + push-to-registry: true \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc7d08c --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +*.tgz +*.swp +.vscode +.idea +dist/ \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..c8e098d --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,97 @@ +version: 2 + +project_name: coredhcp +before: + hooks: + - go install github.com/coredhcp/coredhcp/cmds/coredhcp-generator@latest + - mkdir -p cmd/ + - coredhcp-generator -t generator/coredhcp.go.template -f generator/plugins.txt github.com/OpenCHAMI/coresmd/coresmd github.com/OpenCHAMI/coresmd/bootloop -o cmd/coredhcp.go + - go mod tidy + +builds: + - id: coredhcp + main: ./cmd/ + goos: + - linux + goarch: + - amd64 + - arm64 + goamd64: + - v3 + + # export GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi) + # export BUILD_HOST=$(hostname) + # export GO_VERSION=$(go version | awk '{print $3}') + # export BUILD_USER=$(whoami) + ldflags: + - "-s -w -X main.GitCommit={{.Commit}} \ + -X main.BuildTime={{.Timestamp}} \ + -X main.Version={{.Version}} \ + -X main.GitBranch={{.Branch}} \ + -X main.GitTag={{.Tag}} \ + -X main.GitState={{ .Env.GIT_STATE }} \ + -X main.BuildHost={{ .Env.BUILD_HOST }} \ + -X main.GoVersion={{ .Env.GO_VERSION }} \ + -X main.BuildUser={{ .Env.BUILD_USER }} " + binary: coredhcp + env: + - CGO_ENABLED=0 + + +dockers: + - image_templates: + - &amd64_linux_image ghcr.io/openchami/{{.ProjectName}}:{{ .Tag }}-amd64 + - ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}-amd64 + - ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}-amd64 + use: buildx + build_flag_templates: + - "--pull" + - "--platform=linux/amd64" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + goarch: amd64 + goamd64: v3 + extra_files: + - LICENSE + - README.md + + - image_templates: + - &arm64v8_linux_image ghcr.io/openchami/{{.ProjectName}}:{{ .Tag }}-arm64 + - ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}-arm64 + - ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}-arm64 + use: buildx + build_flag_templates: + - "--pull" + - "--platform=linux/arm64" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + extra_files: + - README.md + - LICENSE + goarch: arm64 + +docker_manifests: + - name_template: "ghcr.io/openchami/{{.ProjectName}}:latest" + image_templates: + - *amd64_linux_image + - *arm64v8_linux_image + + - name_template: "ghcr.io/openchami/{{.ProjectName}}:{{ .Tag }}" + image_templates: + - *amd64_linux_image + - *arm64v8_linux_image + + - name_template: "ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}" + image_templates: + - *amd64_linux_image + - *arm64v8_linux_image + + - name_template: "ghcr.io/openchami/{{.ProjectName}}:{{ .Major }}.{{ .Minor }}" + image_templates: + - *amd64_linux_image + - *arm64v8_linux_image + diff --git a/Dockerfile b/Dockerfile index 76f2f57..3a9783e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,65 +1,20 @@ -################################################################################ -# STAGE 1: Build CoreDHCP -################################################################################ +FROM chainguard/wolfi-base:latest -FROM golang:1.21 AS builder -ARG CGO_ENABLED=1 +RUN apk add --no-cache tini -# -# STEP 1: Clone coredhcp and build coredhcp-generator -# +# Include curl in the final image. +RUN set -ex \ + && apk update \ + && apk add --no-cache curl tini \ + && rm -rf /var/cache/apk/* \ + && rm -rf /tmp/* -RUN git clone https://github.com/coredhcp/coredhcp /coredhcp -WORKDIR /coredhcp +COPY coredhcp /coredhcp -RUN go mod download -RUN go build ./cmds/coredhcp-generator -# -# STEP 2: Copy source tree and generate CoreDHCP main.go -# +# nobody 65534:65534 +USER 65534:65534 -WORKDIR /coresmd -COPY go.mod go.sum ./ -RUN go mod edit -replace=github.com/OpenCHAMI/coresmd=/coresmd -RUN go mod download -COPY . . -RUN ./gen_version.bash +CMD [ "/coredhcp" ] -RUN mkdir /coredhcp-coresmd -WORKDIR /coredhcp-coresmd - -RUN /coredhcp/coredhcp-generator \ - -t /coredhcp/cmds/coredhcp-generator/coredhcp.go.template \ - -f /coredhcp/cmds/coredhcp-generator/core-plugins.txt \ - -o /coredhcp-coresmd/coredhcp.go \ - github.com/OpenCHAMI/coresmd/coresmd \ - github.com/OpenCHAMI/coresmd/bootloop - -# -# STEP 3: Build CoreDHCP -# - -RUN go mod init coredhcp -RUN go mod edit -replace=github.com/coredhcp/coredhcp=/coredhcp -RUN go mod edit -replace=github.com/OpenCHAMI/coresmd=/coresmd -RUN go mod tidy -RUN go build -o coredhcp - -################################################################################ -# STAGE 2: Copy CoreDHCP to final location -################################################################################ - -FROM cgr.dev/chainguard/wolfi-base - -#RUN apk add --no-cache tini - -COPY --from=builder /coredhcp-coresmd/coredhcp /bin/coredhcp - -EXPOSE 67 67/udp - -# Make dir for config file -RUN mkdir -p /etc/coredhcp -VOLUME /etc/coredhcp - -ENTRYPOINT [ "/bin/coredhcp" ] +ENTRYPOINT [ "/sbin/tini", "--" ] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9dc5f7c --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +MIT License + +Copyright © 2024 Triad National Security, LLC. All rights reserved. +This program was produced under U.S. Government contract 89233218CNA000001 +for Los Alamos National Laboratory (LANL), which is operated by Triad +National Security, LLC for the U.S. Department of Energy/National Nuclear +Security Administration. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 5768729..206eb66 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,42 @@ This is meant to be built statically into [CoreDHCP](https://github.com/coredhcp/coredhcp) using the [coredhcp-generator](https://github.com/coredhcp/coredhcp/blob/master/cmds/coredhcp-generator). + +## Build/Install with goreleaser + +This project uses [GoReleaser](https://goreleaser.com/) to automate releases and include additional build metadata such as commit info, build time, and versioning. Below is a guide on how to set up and build the project locally using GoReleaser. + +### Environment Variables + +To include detailed build metadata, ensure the following environment variables are set: + +* __GIT_STATE__: Indicates whether there are uncommitted changes in the working directory. Set to clean if the repository is clean, or dirty if there are uncommitted changes. +* __BUILD_HOST__: The hostname of the machine where the build is being performed. +* __GO_VERSION__: The version of Go used for the build. GoReleaser uses this to ensure consistent Go versioning information. +* __BUILD_USER__: The username of the person or system performing the build. + +Set all the environment variables with: +```bash +export GIT_STATE=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi) +export BUILD_HOST=$(hostname) +export GO_VERSION=$(go version | awk '{print $3}') +export BUILD_USER=$(whoami) +``` + +### Building Locally with GoReleaser + +Once the environment variables are set, you can build the project locally using GoReleaser in snapshot mode (to avoid publishing). + + +Follow the installation instructions from [GoReleaser’s documentation](https://goreleaser.com/install/). + +1. Run GoReleaser in snapshot mode with the --snapshot and --skip-publish flags to create a local build without attempting to release it: + ```bash + goreleaser release --snapshot --skip-publish --clean + ``` +2. Check the dist/ directory for the built binaries, which will include the metadata from the environment variables. You can inspect the binary output to confirm that the metadata was correctly embedded. + + ### Container This repository includes a Dockerfile that builds CoreDHCP with its core plugins diff --git a/bootloop/main.go b/bootloop/main.go index 2e15784..fb786f1 100644 --- a/bootloop/main.go +++ b/bootloop/main.go @@ -9,21 +9,21 @@ import ( "sync" "time" + "github.com/OpenCHAMI/coresmd/internal/debug" + "github.com/OpenCHAMI/coresmd/internal/ipxe" + "github.com/OpenCHAMI/coresmd/internal/version" "github.com/coredhcp/coredhcp/handler" "github.com/coredhcp/coredhcp/logger" "github.com/coredhcp/coredhcp/plugins" "github.com/coredhcp/coredhcp/plugins/allocators" "github.com/coredhcp/coredhcp/plugins/allocators/bitmap" "github.com/insomniacslk/dhcp/dhcpv4" - "github.com/OpenCHAMI/coresmd/internal/debug" - "github.com/OpenCHAMI/coresmd/internal/ipxe" - "github.com/OpenCHAMI/coresmd/internal/version" ) // Record holds an IP lease record type Record struct { - IP net.IP - expires int + IP net.IP + expires int hostname string } @@ -58,7 +58,7 @@ func setup6(args ...string) (handler.Handler6, error) { } func setup4(args ...string) (handler.Handler4, error) { - log.Infof("initializing coresmd/bootloop %s (%s), built %s", version.Version, version.Commit, version.Date) + log.Infof("initializing coresmd/bootloop %s (%s), built %s", version.Version, version.GitCommit, version.BuildTime) // Ensure all required args were passed if len(args) != 4 { @@ -149,8 +149,8 @@ func (p *PluginState) Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) return nil, true } rec := Record{ - IP: ip.IP.To4(), - expires: int(time.Now().Add(p.LeaseTime).Unix()), + IP: ip.IP.To4(), + expires: int(time.Now().Add(p.LeaseTime).Unix()), hostname: hostname, } err = p.saveIPAddress(req.ClientHWAddr, &rec) diff --git a/cmd/coredhcp.go b/cmd/coredhcp.go new file mode 100644 index 0000000..69d3646 --- /dev/null +++ b/cmd/coredhcp.go @@ -0,0 +1,130 @@ +// Copyright 2018-present the CoreDHCP Authors. All rights reserved +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +// This is a generated file, edits should be made in the corresponding source file +// And this file regenerated using `coredhcp-generator --from core-plugins.txt` +package main + +import ( + "fmt" + "io" + "os" + + "github.com/coredhcp/coredhcp/config" + "github.com/coredhcp/coredhcp/logger" + "github.com/coredhcp/coredhcp/server" + + "github.com/coredhcp/coredhcp/plugins" + pl_bootloop "github.com/OpenCHAMI/coresmd/bootloop" + pl_coresmd "github.com/OpenCHAMI/coresmd/coresmd" + pl_autoconfigure "github.com/coredhcp/coredhcp/plugins/autoconfigure" + pl_dns "github.com/coredhcp/coredhcp/plugins/dns" + pl_file "github.com/coredhcp/coredhcp/plugins/file" + pl_ipv6only "github.com/coredhcp/coredhcp/plugins/ipv6only" + pl_leasetime "github.com/coredhcp/coredhcp/plugins/leasetime" + pl_mtu "github.com/coredhcp/coredhcp/plugins/mtu" + pl_nbp "github.com/coredhcp/coredhcp/plugins/nbp" + pl_netmask "github.com/coredhcp/coredhcp/plugins/netmask" + pl_prefix "github.com/coredhcp/coredhcp/plugins/prefix" + pl_range "github.com/coredhcp/coredhcp/plugins/range" + pl_router "github.com/coredhcp/coredhcp/plugins/router" + pl_searchdomains "github.com/coredhcp/coredhcp/plugins/searchdomains" + pl_serverid "github.com/coredhcp/coredhcp/plugins/serverid" + pl_sleep "github.com/coredhcp/coredhcp/plugins/sleep" + pl_staticroute "github.com/coredhcp/coredhcp/plugins/staticroute" + + "github.com/sirupsen/logrus" + flag "github.com/spf13/pflag" +) + +var ( + flagLogFile = flag.StringP("logfile", "l", "", "Name of the log file to append to. Default: stdout/stderr only") + flagLogNoStdout = flag.BoolP("nostdout", "N", false, "Disable logging to stdout/stderr") + flagLogLevel = flag.StringP("loglevel", "L", "info", fmt.Sprintf("Log level. One of %v", getLogLevels())) + flagConfig = flag.StringP("conf", "c", "", "Use this configuration file instead of the default location") + flagPlugins = flag.BoolP("plugins", "P", false, "list plugins") +) + +var logLevels = map[string]func(*logrus.Logger){ + "none": func(l *logrus.Logger) { l.SetOutput(io.Discard) }, + "debug": func(l *logrus.Logger) { l.SetLevel(logrus.DebugLevel) }, + "info": func(l *logrus.Logger) { l.SetLevel(logrus.InfoLevel) }, + "warning": func(l *logrus.Logger) { l.SetLevel(logrus.WarnLevel) }, + "error": func(l *logrus.Logger) { l.SetLevel(logrus.ErrorLevel) }, + "fatal": func(l *logrus.Logger) { l.SetLevel(logrus.FatalLevel) }, +} + +func getLogLevels() []string { + var levels []string + for k := range logLevels { + levels = append(levels, k) + } + return levels +} + +var desiredPlugins = []*plugins.Plugin{ + &pl_bootloop.Plugin, + &pl_coresmd.Plugin, + &pl_autoconfigure.Plugin, + &pl_dns.Plugin, + &pl_file.Plugin, + &pl_ipv6only.Plugin, + &pl_leasetime.Plugin, + &pl_mtu.Plugin, + &pl_nbp.Plugin, + &pl_netmask.Plugin, + &pl_prefix.Plugin, + &pl_range.Plugin, + &pl_router.Plugin, + &pl_searchdomains.Plugin, + &pl_serverid.Plugin, + &pl_sleep.Plugin, + &pl_staticroute.Plugin, +} + +func main() { + flag.Parse() + + if *flagPlugins { + for _, p := range desiredPlugins { + fmt.Println(p.Name) + } + os.Exit(0) + } + + log := logger.GetLogger("main") + fn, ok := logLevels[*flagLogLevel] + if !ok { + log.Fatalf("Invalid log level '%s'. Valid log levels are %v", *flagLogLevel, getLogLevels()) + } + fn(log.Logger) + log.Infof("Setting log level to '%s'", *flagLogLevel) + if *flagLogFile != "" { + log.Infof("Logging to file %s", *flagLogFile) + logger.WithFile(log, *flagLogFile) + } + if *flagLogNoStdout { + log.Infof("Disabling logging to stdout/stderr") + logger.WithNoStdOutErr(log) + } + config, err := config.Load(*flagConfig) + if err != nil { + log.Fatalf("Failed to load configuration: %v", err) + } + // register plugins + for _, plugin := range desiredPlugins { + if err := plugins.RegisterPlugin(plugin); err != nil { + log.Fatalf("Failed to register plugin '%s': %v", plugin.Name, err) + } + } + + // start server + srv, err := server.Start(config) + if err != nil { + log.Fatal(err) + } + if err := srv.Wait(); err != nil { + log.Error(err) + } +} \ No newline at end of file diff --git a/coresmd/main.go b/coresmd/main.go index dd71aa2..96e6b21 100644 --- a/coresmd/main.go +++ b/coresmd/main.go @@ -7,13 +7,13 @@ import ( "net/url" "time" + "github.com/OpenCHAMI/coresmd/internal/debug" + "github.com/OpenCHAMI/coresmd/internal/ipxe" + "github.com/OpenCHAMI/coresmd/internal/version" "github.com/coredhcp/coredhcp/handler" "github.com/coredhcp/coredhcp/logger" "github.com/coredhcp/coredhcp/plugins" "github.com/insomniacslk/dhcp/dhcpv4" - "github.com/OpenCHAMI/coresmd/internal/debug" - "github.com/OpenCHAMI/coresmd/internal/ipxe" - "github.com/OpenCHAMI/coresmd/internal/version" ) type IfaceInfo struct { @@ -44,7 +44,7 @@ func setup6(args ...string) (handler.Handler6, error) { } func setup4(args ...string) (handler.Handler4, error) { - log.Infof("initializing coresmd/coresmd %s (%s), built %s", version.Version, version.Commit, version.Date) + log.Infof("initializing coresmd/coresmd %s (%s), built %s", version.Version, version.GitCommit, version.BuildTime) // Ensure all required args were passed if len(args) != 5 { diff --git a/generator/coredhcp.go.template b/generator/coredhcp.go.template new file mode 100644 index 0000000..6cd4086 --- /dev/null +++ b/generator/coredhcp.go.template @@ -0,0 +1,104 @@ +// Copyright 2018-present the CoreDHCP Authors. All rights reserved +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +{{/* This file is the template source. The following comment obviously doesn't apply here */ -}} +// This is a generated file, edits should be made in the corresponding source file +// And this file regenerated using `coredhcp-generator --from core-plugins.txt` +package main + +import ( + "fmt" + "io" + "os" + + "github.com/coredhcp/coredhcp/config" + "github.com/coredhcp/coredhcp/logger" + "github.com/coredhcp/coredhcp/server" + + "github.com/coredhcp/coredhcp/plugins" +{{- range $plugin := .}} + {{- /* We import all plugins as pl_ to avoid conflicts with reserved keywords */}} + {{importname $plugin}} "{{$plugin}}" +{{- end}} + + "github.com/sirupsen/logrus" + flag "github.com/spf13/pflag" +) + +var ( + flagLogFile = flag.StringP("logfile", "l", "", "Name of the log file to append to. Default: stdout/stderr only") + flagLogNoStdout = flag.BoolP("nostdout", "N", false, "Disable logging to stdout/stderr") + flagLogLevel = flag.StringP("loglevel", "L", "info", fmt.Sprintf("Log level. One of %v", getLogLevels())) + flagConfig = flag.StringP("conf", "c", "", "Use this configuration file instead of the default location") + flagPlugins = flag.BoolP("plugins", "P", false, "list plugins") +) + +var logLevels = map[string]func(*logrus.Logger){ + "none": func(l *logrus.Logger) { l.SetOutput(io.Discard) }, + "debug": func(l *logrus.Logger) { l.SetLevel(logrus.DebugLevel) }, + "info": func(l *logrus.Logger) { l.SetLevel(logrus.InfoLevel) }, + "warning": func(l *logrus.Logger) { l.SetLevel(logrus.WarnLevel) }, + "error": func(l *logrus.Logger) { l.SetLevel(logrus.ErrorLevel) }, + "fatal": func(l *logrus.Logger) { l.SetLevel(logrus.FatalLevel) }, +} + +func getLogLevels() []string { + var levels []string + for k := range logLevels { + levels = append(levels, k) + } + return levels +} + +var desiredPlugins = []*plugins.Plugin{ +{{- range $plugin := .}} + &{{importname $plugin}}.Plugin, +{{- end}} +} + +func main() { + flag.Parse() + + if *flagPlugins { + for _, p := range desiredPlugins { + fmt.Println(p.Name) + } + os.Exit(0) + } + + log := logger.GetLogger("main") + fn, ok := logLevels[*flagLogLevel] + if !ok { + log.Fatalf("Invalid log level '%s'. Valid log levels are %v", *flagLogLevel, getLogLevels()) + } + fn(log.Logger) + log.Infof("Setting log level to '%s'", *flagLogLevel) + if *flagLogFile != "" { + log.Infof("Logging to file %s", *flagLogFile) + logger.WithFile(log, *flagLogFile) + } + if *flagLogNoStdout { + log.Infof("Disabling logging to stdout/stderr") + logger.WithNoStdOutErr(log) + } + config, err := config.Load(*flagConfig) + if err != nil { + log.Fatalf("Failed to load configuration: %v", err) + } + // register plugins + for _, plugin := range desiredPlugins { + if err := plugins.RegisterPlugin(plugin); err != nil { + log.Fatalf("Failed to register plugin '%s': %v", plugin.Name, err) + } + } + + // start server + srv, err := server.Start(config) + if err != nil { + log.Fatal(err) + } + if err := srv.Wait(); err != nil { + log.Error(err) + } +} \ No newline at end of file diff --git a/generator/plugins.txt b/generator/plugins.txt new file mode 100644 index 0000000..1f29098 --- /dev/null +++ b/generator/plugins.txt @@ -0,0 +1,15 @@ +github.com/coredhcp/coredhcp/plugins/autoconfigure +github.com/coredhcp/coredhcp/plugins/dns +github.com/coredhcp/coredhcp/plugins/file +github.com/coredhcp/coredhcp/plugins/ipv6only +github.com/coredhcp/coredhcp/plugins/leasetime +github.com/coredhcp/coredhcp/plugins/mtu +github.com/coredhcp/coredhcp/plugins/netmask +github.com/coredhcp/coredhcp/plugins/nbp +github.com/coredhcp/coredhcp/plugins/prefix +github.com/coredhcp/coredhcp/plugins/range +github.com/coredhcp/coredhcp/plugins/router +github.com/coredhcp/coredhcp/plugins/serverid +github.com/coredhcp/coredhcp/plugins/searchdomains +github.com/coredhcp/coredhcp/plugins/sleep +github.com/coredhcp/coredhcp/plugins/staticroute \ No newline at end of file diff --git a/go.mod b/go.mod index 72cb79d..5c5d00a 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,14 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 github.com/mattn/go-sqlite3 v1.14.22 github.com/sirupsen/logrus v1.9.3 + github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c ) require ( github.com/bits-and-blooms/bitset v1.14.2 // indirect github.com/chappjc/logrus-prefix v0.0.0-20180227015900-3a1d64819adb // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/google/gopacket v1.1.19 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/josharian/native v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -29,13 +31,13 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c // indirect github.com/spf13/viper v1.19.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect diff --git a/go.sum b/go.sum index 396da73..0d8a7d6 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas= @@ -35,6 +37,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= +github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -92,12 +98,23 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -107,8 +124,11 @@ golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/version/version.go b/internal/version/version.go index f10b7cf..1bd8390 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,7 +1,58 @@ package version -var ( - Version = "v0.0.0" - Commit = "0000000" - Date = "0000-00-00:00:00:00" -) +import "fmt" + +// GitCommit stores the latest Git commit hash. +// Set via -ldflags "-X main.GitCommit=$(git rev-parse HEAD)" +var GitCommit string + +// BuildTime stores the build timestamp in UTC. +// Set via -ldflags "-X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" +var BuildTime string + +// Version indicates the version of the binary, such as a release number or semantic version. +// Set via -ldflags "-X main.Version=v1.0.0" +var Version string + +// GitBranch holds the name of the Git branch from which the build was created. +// Set via -ldflags "-X main.GitBranch=$(git rev-parse --abbrev-ref HEAD)" +var GitBranch string + +// GitTag represents the most recent Git tag at build time, if any. +// Set via -ldflags "-X main.GitTag=$(git describe --tags --abbrev=0)" +var GitTag string + +// GitState indicates whether the working directory was "clean" or "dirty" (i.e., with uncommitted changes). +// Set via -ldflags "-X main.GitState=$(if git diff-index --quiet HEAD --; then echo 'clean'; else echo 'dirty'; fi)" +var GitState string + +// BuildHost stores the hostname of the machine where the binary was built. +// Set via -ldflags "-X main.BuildHost=$(hostname)" +var BuildHost string + +// GoVersion captures the Go version used to build the binary. +// Typically, this can be obtained automatically with runtime.Version(), but you can set it manually. +// Set via -ldflags "-X main.GoVersion=$(go version | awk '{print $3}')" +var GoVersion string + +// BuildUser is the username of the person or system that initiated the build process. +// Set via -ldflags "-X main.BuildUser=$(whoami)" +var BuildUser string + +// PrintVersionInfo outputs all versioning information for troubleshooting or version checks. +func PrintVersionInfo() { + fmt.Printf("Version: %s\n", Version) + fmt.Printf("Git Commit: %s\n", GitCommit) + fmt.Printf("Build Time: %s\n", BuildTime) + fmt.Printf("Git Branch: %s\n", GitBranch) + fmt.Printf("Git Tag: %s\n", GitTag) + fmt.Printf("Git State: %s\n", GitState) + fmt.Printf("Build Host: %s\n", BuildHost) + fmt.Printf("Go Version: %s\n", GoVersion) + fmt.Printf("Build User: %s\n", BuildUser) +} + +func VersionInfo() string { + return fmt.Sprintf("Version: %s, Git Commit: %s, Build Time: %s, Git Branch: %s, Git Tag: %s, Git State: %s, Build Host: %s, Go Version: %s, Build User: %s", + Version, GitCommit, BuildTime, GitBranch, GitTag, GitState, BuildHost, GoVersion, BuildUser) +}