Skip to content

Commit ba758b0

Browse files
committed
feat: create client config and mongo replica
1 parent 61835a3 commit ba758b0

8 files changed

+139
-60
lines changed

README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ Version from [here](https://puppetdoc.anytype.io/api/v1/prod-any-sync-compatible
2323
Need to create replica set for MongoDB. Manually or with some script.
2424
Check that address should be same as when we will start to use it?
2525

26-
```bash
2726
```bash
2827
docker build -t any --progress=plain -f docker/Dockerfile.all-in-one .
2928

30-
docker run -it \
29+
docker run --rm -it \
30+
-e ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS="192.168.100.9" \
3131
-p 33010-33013:33010-33013 \
3232
-p 33020-33023:33020-33023/udp \
3333
-p 27017:27017 \
@@ -36,11 +36,13 @@ docker run -it \
3636
--name any-sync-bundle \
3737
any:latest
3838
```
39-
```
4039

4140
## TODO
4241

42+
- https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes#non-bsd
4343
- Create first tech version
44+
- Improve loggings and add prefix for each service, like `any-sync-coordinator:`
45+
- Maybe replace supervisor with some simple script
4446
- Add release with binaries and containers for all platforms
4547
- use port range to public for simplicity
4648
- check other docker build, like docker-mastodon

cmd/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func cmdConfigClient() *cli.Command {
9292

9393
// Generate and write client configuration
9494
bundleConfig := bundleCfg.Load(bundleCfgPath)
95-
clientCfgData, err := bundleConfig.ClientConfig()
95+
clientCfgData, err := bundleConfig.YamlClientConfig()
9696
if err != nil {
9797
return fmt.Errorf("failed to generate client configuration: %w", err)
9898
}

cmd/mongo.go

+43-33
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"context"
55
"fmt"
6+
"net/url"
67
"time"
78

89
"github.com/urfave/cli/v2"
@@ -17,6 +18,10 @@ const (
1718
mongoConnectTimeout = 10 * time.Second
1819
mongoCommandTimeout = 5 * time.Second
1920
mongoStabilizeWaitTime = 5 * time.Second
21+
22+
// Default MongoDB parameters
23+
defaultMongoReplica = "rs0"
24+
defaultMongoURI = "mongodb://127.0.0.1:27017/"
2025
)
2126

2227
func cmdMongo(ctx context.Context) *cli.Command {
@@ -35,56 +40,56 @@ func cmdMongoInit(ctx context.Context) *cli.Command {
3540
&cli.StringFlag{
3641
Name: "replica",
3742
Aliases: []string{"r"},
38-
Value: "rs0",
43+
Value: defaultMongoReplica,
3944
Usage: "Name of the replica set",
4045
EnvVars: []string{"ANY_SYNC_BUNDLE_MONGO_REPLICA"},
4146
},
4247
&cli.StringFlag{
4348
Name: "uri",
4449
Aliases: []string{"u"},
45-
Value: "mongodb://127.0.0.1:27017/",
50+
Value: defaultMongoURI,
4651
Usage: "MongoDB connection URI",
4752
EnvVars: []string{"ANY_SYNC_BUNDLE_MONGO_URI"},
4853
},
4954
},
50-
Action: initReplicaSetAction(ctx),
55+
Action: func(cCtx *cli.Context) error {
56+
replica := cCtx.String("replica")
57+
mongoURI := cCtx.String("uri")
58+
59+
return initReplicaSetAction(ctx, replica, mongoURI)
60+
},
5161
}
5262
}
5363

54-
func initReplicaSetAction(ctx context.Context) cli.ActionFunc {
55-
// Fibonacci sequence for exponential backoff, limit number of attempts
64+
func initReplicaSetAction(ctx context.Context, replica, mongoURI string) error {
65+
// For exponential backoff, limit number of attempts
5666
retryDelays := []int{1, 2, 3, 5, 8, 13, 21, 34, 55, 89}
5767

58-
return func(cCtx *cli.Context) error {
59-
replica := cCtx.String("replica")
60-
mongoURI := cCtx.String("uri")
61-
62-
log.Info("initializing mongo replica set",
63-
zap.String("uri", mongoURI),
64-
zap.String("replica", replica))
65-
66-
// Direct - before we have a replica set, we need it
67-
clientOpts := options.Client().ApplyURI(mongoURI).SetDirect(true)
68-
69-
for _, delay := range retryDelays {
70-
err := tryInitReplicaSet(ctx, clientOpts, replica)
71-
if err == nil {
72-
log.Info("successfully initialized mongo replica set")
73-
return nil
74-
} else if ctx.Err() != nil {
75-
log.Error("context canceled while initializing mongo replica set", zap.Error(ctx.Err()))
76-
return ctx.Err()
77-
}
78-
79-
log.Warn("failed to initialize mongo replica set, retrying...",
80-
zap.Error(err),
81-
zap.Int("delay_seconds", delay))
82-
83-
time.Sleep(time.Duration(delay) * time.Second)
68+
log.Info("initializing mongo replica set",
69+
zap.String("uri", mongoURI),
70+
zap.String("replica", replica))
71+
72+
// Direct - before we have a replica set, we need it
73+
clientOpts := options.Client().ApplyURI(mongoURI).SetDirect(true)
74+
75+
for _, delay := range retryDelays {
76+
err := tryInitReplicaSet(ctx, clientOpts, replica)
77+
if err == nil {
78+
log.Info("successfully initialized mongo replica set")
79+
return nil
80+
} else if ctx.Err() != nil {
81+
log.Error("context canceled while initializing mongo replica set", zap.Error(ctx.Err()))
82+
return ctx.Err()
8483
}
8584

86-
return fmt.Errorf("failed to initialize mongo replica set after all retries")
85+
log.Warn("failed to initialize mongo replica set, retrying...",
86+
zap.Error(err),
87+
zap.Int("delay_seconds", delay))
88+
89+
time.Sleep(time.Duration(delay) * time.Second)
8790
}
91+
92+
return fmt.Errorf("failed to initialize mongo replica set after all retries")
8893
}
8994

9095
func tryInitReplicaSet(ctx context.Context, clientOpts *options.ClientOptions, replica string) error {
@@ -116,13 +121,18 @@ func tryInitReplicaSet(ctx context.Context, clientOpts *options.ClientOptions, r
116121
}
117122

118123
func initNewReplicaSet(ctx context.Context, client *mongo.Client, replica, uri string) error {
124+
parsedURL, err := url.Parse(uri)
125+
if err != nil {
126+
return fmt.Errorf("failed to parse mongodb uri: %w", err)
127+
}
128+
119129
cmd := bson.D{
120130
{Key: "replSetInitiate", Value: bson.D{
121131
{Key: "_id", Value: replica},
122132
{Key: "members", Value: bson.A{
123133
bson.D{
124134
{Key: "_id", Value: 0},
125-
{Key: "host", Value: uri},
135+
{Key: "host", Value: parsedURL.Host},
126136
},
127137
}},
128138
}},

cmd/start.go

+73-13
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ import (
1414
"github.com/urfave/cli/v2"
1515
"go.uber.org/zap"
1616

17-
bundleCfg "github.com/grishy/any-sync-bundle/config"
17+
bundleConfig "github.com/grishy/any-sync-bundle/config"
1818
bundleNode "github.com/grishy/any-sync-bundle/node"
1919
)
2020

21+
const (
22+
fIsInitMongoRs = "init-mongo-rs"
23+
)
24+
2125
type node struct {
2226
name string
2327
app *app.App
@@ -27,23 +31,54 @@ const serviceShutdownTimeout = 30 * time.Second
2731

2832
func cmdStart(ctx context.Context) *cli.Command {
2933
return &cli.Command{
30-
Name: "start",
31-
Usage: "Start bundle services",
34+
Name: "start",
35+
Usage: "Start bundle services",
36+
Flags: []cli.Flag{
37+
&cli.BoolFlag{
38+
Name: fIsInitMongoRs,
39+
Usage: "Initialize MongoDB replica set",
40+
EnvVars: []string{"ANY_SYNC_BUNDLE_INIT_MONGO_RS"},
41+
},
42+
},
3243
Action: startAction(ctx),
3344
}
3445
}
3546

3647
func startAction(ctx context.Context) cli.ActionFunc {
3748
return func(cCtx *cli.Context) error {
38-
printWelcome()
49+
clientCfgPath := cCtx.String(fGlobalClientConfigPath)
50+
isInitMongoRs := cCtx.Bool(fIsInitMongoRs)
51+
52+
printWelcomeMsg()
3953

4054
// Load or create bundle configuration
41-
cfgBundle := loadOrCreateConfig(cCtx, log)
55+
bundleCfg := loadOrCreateConfig(cCtx, log)
4256

43-
// TODO: Write client config if not exists
57+
// Create client configuration if not exists
58+
if _, err := os.Stat(clientCfgPath); err != nil {
59+
log.Warn("client configuration not found, creating new one")
60+
yamlData, err := bundleCfg.YamlClientConfig()
61+
if err != nil {
62+
return fmt.Errorf("failed to generate client config: %w", err)
63+
}
4464

45-
// Initialize service instances
46-
cfgNodes := cfgBundle.NodeConfigs()
65+
if err := os.WriteFile(clientCfgPath, yamlData, configFileMode); err != nil {
66+
return fmt.Errorf("failed to write client config: %w", err)
67+
}
68+
69+
log.Info("client configuration written", zap.String("path", clientCfgPath))
70+
}
71+
72+
if isInitMongoRs {
73+
log.Info("initializing MongoDB replica set")
74+
75+
if err := initReplicaSetAction(ctx, defaultMongoReplica, defaultMongoURI); err != nil {
76+
return fmt.Errorf("failed to initialize MongoDB replica set: %w", err)
77+
}
78+
}
79+
80+
// Initialize nodes' instances
81+
cfgNodes := bundleCfg.NodeConfigs()
4782

4883
apps := []node{
4984
{name: "coordinator", app: bundleNode.NewCoordinatorApp(cfgNodes.Coordinator)},
@@ -56,27 +91,30 @@ func startAction(ctx context.Context) cli.ActionFunc {
5691
return err
5792
}
5893

94+
printStartupMsg()
95+
5996
// Wait for shutdown signal
6097
<-ctx.Done()
6198

6299
shutdownServices(apps)
100+
printShutdownMsg()
63101

64102
log.Info("→ Goodbye!")
65103
return nil
66104
}
67105
}
68106

69-
func loadOrCreateConfig(cCtx *cli.Context, log logger.CtxLogger) *bundleCfg.Config {
107+
func loadOrCreateConfig(cCtx *cli.Context, log logger.CtxLogger) *bundleConfig.Config {
70108
cfgPath := cCtx.String(fGlobalBundleConfigPath)
71109
log.Info("loading config")
72110

73111
if _, err := os.Stat(cfgPath); err == nil {
74112
log.Info("loaded existing config")
75-
return bundleCfg.Load(cfgPath)
113+
return bundleConfig.Load(cfgPath)
76114
}
77115

78116
log.Info("creating new config")
79-
return bundleCfg.CreateWrite(&bundleCfg.CreateOptions{
117+
return bundleConfig.CreateWrite(&bundleConfig.CreateOptions{
80118
CfgPath: cfgPath,
81119
StorePath: cCtx.String(fGlobalStoragePath),
82120
MongoURI: cCtx.String(fGlobalInitMongoURI),
@@ -97,7 +135,6 @@ func startServices(ctx context.Context, apps []node) error {
97135
log.Info("✓ service started successfully", zap.String("name", a.name))
98136
}
99137

100-
log.Info("↑ service startup complete")
101138
return nil
102139
}
103140

@@ -119,7 +156,7 @@ func shutdownServices(apps []node) {
119156
}
120157
}
121158

122-
func printWelcome() {
159+
func printWelcomeMsg() {
123160
fmt.Printf(`
124161
┌───────────────────────────────────────────────────────────────────┐
125162
@@ -148,3 +185,26 @@ func printWelcome() {
148185
└───────────────────────────────────────────────────────────────────┘
149186
`)
150187
}
188+
189+
func printStartupMsg() {
190+
fmt.Printf(`
191+
┌───────────────────────────────────────────────────────────────────┐
192+
193+
AnySync Bundle is ready!
194+
All services are running.
195+
Press Ctrl+C to stop services.
196+
197+
└───────────────────────────────────────────────────────────────────┘
198+
`)
199+
}
200+
201+
func printShutdownMsg() {
202+
fmt.Printf(`
203+
┌───────────────────────────────────────────────────────────────────┐
204+
205+
AnySync Bundle shutdown complete!
206+
All services are stopped.
207+
208+
└───────────────────────────────────────────────────────────────────┘
209+
`)
210+
}

config/client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func (bc *Config) convertExternalAddrs(listen NodeShared) []string {
4040
return addrs
4141
}
4242

43-
func (bc *Config) ClientConfig() ([]byte, error) {
43+
func (bc *Config) YamlClientConfig() ([]byte, error) {
4444
network := nodeconf.Configuration{
4545
Id: bc.ConfigID,
4646
NetworkId: bc.NetworkID,

docker/Dockerfile

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ COPY ./ ./
88
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o any-sync-bundle
99

1010
FROM gcr.io/distroless/static-debian12
11-
# TODO: Use go-avahi-way of build
12-
LABEL maintainer="Sergei G. <[email protected]>"
13-
LABEL description="Anytype Self-Hosting: All-in-One Prepared for You"
1411

1512
COPY --from=build /app/any-sync-bundle /usr/local/bin/any-sync-bundle
1613

@@ -21,4 +18,5 @@ EXPOSE 33010-33013 \
2118

2219
VOLUME /data
2320

24-
CMD ["/usr/local/bin/any-sync-bundle"]
21+
ENTRYPOINT ["/usr/local/bin/any-sync-bundle"]
22+
CMD ["start"]

docker/Dockerfile.all-in-one

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ COPY ./ ./
88
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o any-sync-bundle
99

1010
FROM docker.io/redis/redis-stack-server:7.4.0-v2
11-
LABEL maintainer="Sergei G. <[email protected]>"
12-
LABEL description="Anytype Self-Hosting: All-in-One Prepared for You"
1311

1412
# Install supervisor and tool to intall mongoDB
1513
RUN DEBIAN_FRONTEND=noninteractive \

0 commit comments

Comments
 (0)