Skip to content

Commit 943c7d3

Browse files
authored
Bunnynet Magic Containers as a supported runtime (#10)
- Adds automatic detection of [BunnyNet's Magic Containers](https://docs.bunny.net/docs/magic-containers-overview) as a cloud runtime. - Adds `HACHYBOOP_TEST_FREQUENCY_SECONDS` (integer) to control how often tests run in seconds.
1 parent 12837b4 commit 943c7d3

File tree

7 files changed

+146
-23
lines changed

7 files changed

+146
-23
lines changed

.envrc.example

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88
export HACHYBOOP_OBSERVER_ID=esk
99
export HACHYBOOP_OBSERVER_REGION=exandria
1010

11-
## DNS Configuration
11+
## Testing parameters
1212

13-
### Resolvers and questions - what DNS entries are you poking, and who are you asking?
13+
### Frequency
14+
15+
export HACHYBOOP_TEST_FREQUENCY_SECONDS=300
16+
17+
### DNS Configuration
18+
19+
#### Resolvers and questions - what DNS entries are you poking, and who are you asking?
1420
export HACHYBOOP_RESOLVERS=91.200.176.1:53,8.8.8.8:53
1521
export HACHYBOOP_QUESTIONS=hachyderm.io,hachyderm.wtf
1622

Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,20 @@ FROM alpine
2222

2323
COPY --from=build /out/hachyboop /bin
2424

25+
# Envvars
26+
27+
ENV HACHYBOOP_OBSERVER_ID=anonymous-hachyfriend
28+
ENV HACHYBOOP_OBSERVER_REGION=analog-nowhere
29+
ENV HACHYBOOP_RESOLVERS=91.200.176.1:53,8.8.8.8:53
30+
ENV HACHYBOOP_QUESTIONS=hachyderm.io
31+
ENV HACHYBOOP_S3_WRITER_ENABLED=false
32+
ENV HACHYBOOP_S3_ENDPOINT=replace-me.local
33+
ENV HACHYBOOP_S3_BUCKET=replace-me
34+
ENV HACHYBOOP_S3_PATH=replace-me/with-something
35+
ENV HACHYBOOP_S3_ACCESS_KEY_ID=replace-me
36+
ENV HACHYBOOP_S3_SECRET_ACCESS_KEY=replace-me
37+
ENV HACHYBOOP_LOCAL_WRITER_ENABLED=true
38+
ENV HACHYBOOP_LOCAL_RESULTS_PATH=data
39+
ENV HACHYBOOP_TEST_FREQUENCY_SECONDS=300
40+
2541
ENTRYPOINT [ "/bin/hachyboop" ]

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ compile: ## Compile for the local architecture ⚙
4040
-X 'github.com/$(org)/$(target).Name=$(target)'" \
4141
-o $(target) cmd/*.go
4242

43+
.PHONY: run
44+
run: compile
45+
./hachyboop
46+
4347
.PHONY: container
4448
container:
4549
docker build . -t $(target):latest
@@ -57,6 +61,11 @@ runcontainer: container
5761
-e HACHYBOOP_RESOLVERS \
5862
-e HACHYBOOP_QUESTIONS \
5963
-e HACHYBOOP_LOCAL_RESULTS_PATH \
64+
-e BUNNYNET_MC_REGION \
65+
-e BUNNYNET_MC_ZONE \
66+
-e BUNNYNET_MC_APPID \
67+
-e BUNNYNET_MC_PODID \
68+
-e HACHYBOOP_TEST_FREQUENCY_SECONDS \
6069
--mount type=bind,src=data/,dst=/data \
6170
$(target):latest
6271

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,15 @@ Hachyboop provides several environment variables to configure how it behaves. If
5656
export HACHYBOOP_OBSERVER_ID=esk # unique (or not) identifier for you. recommendation is you provide a pseudonym or generated unique ID.
5757
export HACHYBOOP_OBSERVER_REGION=exandria # see below, region code that most closely matches where you are
5858

59-
## DNS Configuration
59+
## Testing parameters
6060

61-
### Resolvers and questions - what DNS entries are you poking, and who are you asking?
61+
### Frequency
62+
63+
export HACHYBOOP_TEST_FREQUENCY_SECONDS=300 # how often should we run our rtest?
64+
65+
### DNS Configuration
66+
67+
#### Resolvers and questions - what DNS entries are you poking, and who are you asking?
6268
export HACHYBOOP_RESOLVERS=91.200.176.1:53,8.8.8.8:53 # comma-separated, requires port number, e.g. 8.8.8.8:53
6369
export HACHYBOOP_QUESTIONS=hachyderm.io # which records do you want to test
6470

@@ -110,6 +116,14 @@ When choosing a value, choose the value that most closely matches how you would
110116
- australia-east
111117
- australia-west
112118

119+
### Runtime cloud detection
120+
121+
Hachyboop can detect some clouds based on metadata available to it. It will detect:
122+
123+
- **Bunny.net Magic Containers** - if `BUNNYNET_MC_PODIP` is present in the environment it will set (and override):
124+
- `HACHYBOOP_OBSERVER_ID` = `BUNNYNET_MC_APPID` / `BUNNYNET_MC_PODIP`
125+
- `HACHYBOOP_OBSERVER_REGION` = `BUNNYNET_MC_REGION` / `BUNNYNET_MC_ZONE`
126+
113127
## Example of collected data
114128

115129
> [!IMPORTANT]
@@ -120,4 +134,3 @@ Here's a screenshot of the data we collect with `hachyboop`. The `observedby` an
120134
![image](https://github.com/user-attachments/assets/3e1a8ddf-7777-4336-8139-b233e53839c6)
121135

122136

123-

cmd/main.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package main
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"os"
23+
"strconv"
2224
"strings"
2325

2426
hb "github.com/hachyderm/hachyboop"
@@ -37,8 +39,9 @@ var banner = `
3739
`
3840

3941
var cfg = &service.HachyboopOptions{
40-
S3Output: &service.S3Options{},
41-
FileOutput: &service.FileOptions{},
42+
S3Output: &service.S3Options{},
43+
FileOutput: &service.FileOptions{},
44+
RuntimeCloudProviderMetadata: &service.RuntimeCloudProviderMetadata{},
4245
}
4346

4447
func main() {
@@ -75,6 +78,23 @@ A longer sentence, about how exactly to use this program`,
7578
cfg.Resolvers = strings.Split(cfg.ResolversRaw, ",")
7679
cfg.ObservationHandler = make(chan *service.HachyboopDnsObservation, 32)
7780

81+
testSec, err := strconv.Atoi(cfg.TestFrequencySecondsRaw)
82+
if err != nil {
83+
logrus.WithField("rawValue", cfg.TestFrequencySecondsRaw).Warn("couldn't convert HACHYBOOP_TEST_FREQUENCY_SECONDS to an int, defaulting to 300")
84+
testSec = 300
85+
}
86+
cfg.TestFrequencySeconds = testSec
87+
logrus.WithField("seconds", cfg.TestFrequencySeconds).Info("Running tests every x seconds")
88+
89+
// TODO make this dynamic and less hardcoded
90+
if cfg.RuntimeCloudProviderMetadata.BunnyPodId != "" {
91+
logrus.Info("Cloud provider: Bunny Magic Container Runtime detected")
92+
cfg.ObservationRegion = fmt.Sprintf("%s/%s", cfg.RuntimeCloudProviderMetadata.BunnyRegion, cfg.RuntimeCloudProviderMetadata.BunnyZone)
93+
cfg.ObserverId = fmt.Sprintf("%s/%s", cfg.RuntimeCloudProviderMetadata.BunnyAppId, cfg.RuntimeCloudProviderMetadata.BunnyPodId)
94+
}
95+
96+
logrus.WithField("observer", cfg.ObserverId).WithField("region", cfg.ObservationRegion).Debug("Set region and observer")
97+
7898
// TODO validate at least one question & one resolver
7999

80100
return hachyboopInstance.Run()
@@ -89,7 +109,7 @@ A longer sentence, about how exactly to use this program`,
89109
}
90110
logrus.Info("==========================================================================")
91111

92-
logrus.Debugf("Parsing config")
112+
logrus.Debug("Parsing config")
93113

94114
// Load environment variables
95115
err = Environment()
@@ -101,7 +121,7 @@ A longer sentence, about how exactly to use this program`,
101121
// Arbitrary (non-error) pre load
102122
BeforeAppRun()
103123

104-
logrus.Debugf("Entering main app loop")
124+
logrus.Debug("Entering main app loop")
105125

106126
// Runtime
107127
err = app.Run(context.Background(), os.Args)

cmd/main_env.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,30 @@ var (
8989
Destination: &cfg.ObservationRegion,
9090
Required: false,
9191
},
92+
{
93+
Name: "BUNNYNET_MC_REGION",
94+
Destination: &cfg.RuntimeCloudProviderMetadata.BunnyRegion,
95+
Value: "",
96+
Required: false,
97+
},
98+
{
99+
Name: "BUNNYNET_MC_PODID",
100+
Destination: &cfg.RuntimeCloudProviderMetadata.BunnyPodId,
101+
Value: "",
102+
Required: false,
103+
},
104+
{
105+
Name: "BUNNYNET_MC_APPID",
106+
Destination: &cfg.RuntimeCloudProviderMetadata.BunnyAppId,
107+
Value: "",
108+
Required: false,
109+
},
110+
{
111+
Name: "BUNNYNET_MC_ZONE",
112+
Destination: &cfg.RuntimeCloudProviderMetadata.BunnyZone,
113+
Value: "",
114+
Required: false,
115+
},
92116
{
93117
Name: "HACHYBOOP_QUESTIONS",
94118
Value: "hachyderm.io",
@@ -101,6 +125,12 @@ var (
101125
Destination: &cfg.ResolversRaw,
102126
Required: false,
103127
},
128+
{
129+
Name: "HACHYBOOP_TEST_FREQUENCY_SECONDS",
130+
Value: "300",
131+
Destination: &cfg.TestFrequencySecondsRaw,
132+
Required: false,
133+
},
104134
}
105135
)
106136

@@ -113,12 +143,16 @@ type EnvironmentVariable struct {
113143

114144
func Environment() error {
115145
for _, v := range registry {
116-
v.Value = os.Getenv(v.Name)
117-
if v.Required && v.Value == "" {
146+
readValue := os.Getenv(v.Name)
147+
if v.Required && readValue == "" {
118148
// If required and the variable is empty
119149
return fmt.Errorf("empty or undefined environmental variable [%s]", v.Name)
120150
}
121-
*v.Destination = v.Value
151+
152+
if readValue != "" {
153+
v.Value = readValue // we don't use v.Value but why not
154+
*v.Destination = readValue
155+
}
122156
}
123157
return nil
124158
}

internal/service/hachyboop.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,18 @@ import (
3939

4040
// Configuration options for Hachyboop.
4141
type HachyboopOptions struct {
42-
Verbose bool
43-
S3Output *S3Options
44-
FileOutput *FileOptions
45-
ObserverId string
46-
ObservationRegion string
47-
QuestionsRaw string // raw input from env/args
48-
Questions []string
49-
ResolversRaw string
50-
Resolvers []string
42+
Verbose bool
43+
S3Output *S3Options
44+
FileOutput *FileOptions
45+
RuntimeCloudProviderMetadata *RuntimeCloudProviderMetadata
46+
ObserverId string
47+
ObservationRegion string
48+
QuestionsRaw string // raw input from env/args
49+
Questions []string
50+
ResolversRaw string
51+
Resolvers []string
52+
TestFrequencySecondsRaw string
53+
TestFrequencySeconds int
5154

5255
ObservationHandler chan *HachyboopDnsObservation
5356
}
@@ -72,8 +75,26 @@ type FileOptions struct {
7275
ParquetWriter *writer.ParquetWriter
7376
}
7477

78+
// Mostly for runtime provider contextual info
79+
type RuntimeCloudProviderMetadata struct {
80+
// eventually we'll do something magical/dynamic to choose a provider based on detected runtime. for now, keeping it simple.
81+
// Bunny stuff https://docs.bunny.net/docs/magic-containers-app-metadata
82+
// BUNNYNET_MC_REGION
83+
BunnyRegion string
84+
// BUNNYNET_MC_PODID
85+
BunnyPodId string
86+
// BUNNYNET_MC_ZONE
87+
BunnyZone string
88+
// BUNNYNET_MC_APPID
89+
BunnyAppId string
90+
}
91+
7592
// True if we should output to S3.
7693
func (s *S3Options) Enabled() bool {
94+
if s.EnabledRaw == "" {
95+
return false
96+
}
97+
7798
res, err := strconv.ParseBool(s.EnabledRaw)
7899

79100
if err != nil {
@@ -85,6 +106,10 @@ func (s *S3Options) Enabled() bool {
85106

86107
// True if we should output to a local file.
87108
func (f *FileOptions) Enabled() bool {
109+
if f.EnabledRaw == "" {
110+
return false
111+
}
112+
88113
res, err := strconv.ParseBool(f.EnabledRaw)
89114

90115
if err != nil {
@@ -146,8 +171,8 @@ func (hb *Hachyboop) Run() error {
146171
for Enabled {
147172
hb.queryResolvers(resolvers)
148173

149-
// TODO from config
150-
time.Sleep(10 * time.Second)
174+
logrus.WithField("seconds", hb.Options.TestFrequencySeconds).Debug("Sleeping")
175+
time.Sleep(time.Duration(hb.Options.TestFrequencySeconds) * time.Second)
151176
}
152177
return nil
153178
}

0 commit comments

Comments
 (0)