Skip to content

Commit a644d5d

Browse files
authored
Merge pull request #14 from ncode/juliano/improvements
cleanups, systemd command and move to slog
2 parents 88a5278 + a6c41d0 commit a644d5d

File tree

11 files changed

+1091
-480
lines changed

11 files changed

+1091
-480
lines changed

README.md

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,124 @@
1-
# TagIt
1+
# TagIt
2+
23
[![Go Report Card](https://goreportcard.com/badge/github.com/ncode/tagit)](https://goreportcard.com/report/github.com/ncode/tagit)
34
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
45
[![codecov](https://codecov.io/gh/ncode/tagit/graph/badge.svg?token=ISXEH274YD)](https://codecov.io/gh/ncode/tagit)
56

7+
TagIt is a tool that updates Consul service registration tags with outputs of a script. It copies the current service registration and appends the output of the script line by line as tags, while keeping the original tags.
8+
9+
## Table of Contents
10+
11+
- [Why TagIt?](#why-tagit)
12+
- [Installation](#installation)
13+
- [Usage](#usage)
14+
- [Run Command](#run-command)
15+
- [Cleanup Command](#cleanup-command)
16+
- [Systemd Command](#systemd-command)
17+
- [How It Works](#how-it-works)
18+
- [Examples](#examples)
19+
- [Contributing](#contributing)
20+
- [License](#license)
621

7-
Update consul registration tags with outputs of a script.
8-
It copies the current service registration and appends the output of the script line by line as tags, while keeping the original tags.
22+
## Why TagIt?
923

10-
## Why?
24+
TagIt addresses a feature that's currently missing from Consul. You can read more about the need for this functionality in [this Consul issue](https://github.com/hashicorp/consul/issues/1048).
1125

12-
Basically because it's a very useful feature that is missing from consul. Read more about it [here](https://github.com/hashicorp/consul/issues/1048).
13-
A few scenarios where this can be useful:
26+
Here are some scenarios where TagIt can be useful:
1427

15-
1. Your databases are under mydb.service.consul, and you would like to ensure that all the writes go to the leader
16-
1. You run a script that checks the leader and updates the tag
17-
2. You have a service that is not consul aware, but you would like to use consul for service discovery
18-
1. You run a script that checks the service and updates the tags
19-
3. You have a load or a webserver, and you would like to have tags for all vhosts that are served by this server
20-
1. You run a script that checks the vhosts and updates the tags
21-
4. Pretty much any services that are not consul aware, but you would like to use consul for service discovery
22-
1. You run a script that checks the service and updates the tags
28+
1. **Database Leader Tagging**: Ensure all writes go to the leader by tagging it appropriately.
29+
2. **Non-Consul-Aware Service Discovery**: Use Consul for service discovery with services that aren't Consul-aware.
30+
3. **Web Server VHost Tagging**: Tag all vhosts served by a web server or load balancer.
31+
4. **Generic Service Tagging**: Tag any services for Consul-based service discovery.
2332

24-
## How to test it?
33+
## Installation
34+
35+
To install TagIt, use the following commands:
2536

2637
```bash
27-
$ git clone github.com/ncode/tagit
28-
$ cd configs/development
29-
$ make
38+
$ git clone https://github.com/ncode/tagit
39+
$ cd tagit
40+
$ go build
3041
```
3142

43+
## Usage
44+
45+
TagIt provides three main commands: `run`, `cleanup`, and `systemd`.
46+
47+
### Run Command
48+
49+
The `run` command starts TagIt and continuously updates the tags based on the script output:
50+
51+
```bash
52+
$ ./tagit run --consul-addr=127.0.0.1:8500 --service-id=my-service1 --script=./examples/tagit/example.sh --interval=5s --tag-prefix=tagit
53+
```
54+
55+
### Cleanup Command
56+
57+
The `cleanup` command removes all tags with the specified prefix from the service:
58+
59+
```bash
60+
$ ./tagit cleanup --consul-addr=127.0.0.1:8500 --service-id=my-service1 --tag-prefix=tagit
61+
```
62+
63+
### Systemd Command
64+
65+
The `systemd` command generates a systemd service file for TagIt:
66+
67+
```bash
68+
./tagit systemd --service-id=my-service1 --script=./examples/tagit/example.sh --tag-prefix=tagit --interval=5s --user=tagit --group=tagit
69+
```
70+
71+
This command will output a systemd service file that you can use to run TagIt as a system service.
72+
73+
## How It Works
74+
75+
TagIt interacts with Consul as follows:
76+
3277
```mermaid
3378
sequenceDiagram
3479
participant tagit
3580
participant consul
3681
loop execute script on interval
37-
tagit->>consul: Do you have a service with id my-service1?
38-
consul->>tagit: Yes, here it is and that's the current registration
39-
tagit->>consul: Update current registration adding or removing prefixed tags wiht the output of the script
82+
tagit->>consul: Query service with ID
83+
consul->>tagit: Return current service registration
84+
tagit->>tagit: Execute script and process output
85+
tagit->>consul: Update service registration with new tags
4086
end
4187
```
4288

43-
## Todo
89+
## Examples
90+
91+
Here's an example of how to test TagIt:
92+
93+
1. Start a Consul agent in development mode:
94+
```bash
95+
consul agent -dev &
96+
```
97+
98+
2. Register a service with Consul:
99+
```bash
100+
curl --request PUT --data @examples/consul/my-service1.json http://127.0.0.1:8500/v1/agent/service/register
101+
```
102+
103+
3. Run TagIt:
104+
```bash
105+
./tagit run --consul-addr=127.0.0.1:8500 --service-id=my-service1 --script=./examples/tagit/example.sh --interval=5s --tag-prefix=tagit
106+
```
107+
108+
4. Generate a systemd service file:
109+
```bash
110+
./tagit systemd --service-id=my-service1 --script=./examples/tagit/example.sh --tag-prefix=tagit --interval=5s --user=tagit --group=tagit > /etc/systemd/system/tagit-my-service1.service
111+
```
112+
113+
5. Clean up the tags:
114+
```bash
115+
./tagit cleanup --consul-addr=127.0.0.1:8500 --service-id=my-service1 --tag-prefix=tagit
116+
```
117+
118+
## Contributing
119+
120+
Contributions to TagIt are welcome! Please feel free to submit a Pull Request.
121+
122+
## License
44123

45-
- [ ] Adds a systemd unit file generator
124+
TagIt is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details.

cmd/cleanup.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,55 @@ limitations under the License.
1616
package cmd
1717

1818
import (
19-
"fmt"
19+
"log/slog"
20+
"os"
21+
2022
"github.com/hashicorp/consul/api"
2123
"github.com/ncode/tagit/pkg/tagit"
2224
"github.com/spf13/cobra"
23-
"os"
2425
)
2526

2627
// cleanupCmd represents the cleanup command
2728
var cleanupCmd = &cobra.Command{
2829
Use: "cleanup",
2930
Short: "cleanup removes all services with the tag prefix from a given consul service",
3031
Run: func(cmd *cobra.Command, args []string) {
32+
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
33+
Level: slog.LevelInfo,
34+
}))
35+
3136
config := api.DefaultConfig()
3237
config.Address = cmd.InheritedFlags().Lookup("consul-addr").Value.String()
3338
config.Token = cmd.InheritedFlags().Lookup("token").Value.String()
39+
3440
consulClient, err := api.NewClient(config)
3541
if err != nil {
36-
fmt.Printf("error creating consul client: %s", err.Error())
42+
logger.Error("Failed to create Consul client", "error", err)
3743
os.Exit(1)
3844
}
3945

46+
serviceID := cmd.InheritedFlags().Lookup("service-id").Value.String()
47+
tagPrefix := cmd.InheritedFlags().Lookup("tag-prefix").Value.String()
48+
4049
t := tagit.New(
4150
tagit.NewConsulAPIWrapper(consulClient),
4251
&tagit.CmdExecutor{},
43-
cmd.InheritedFlags().Lookup("service-id").Value.String(),
44-
cmd.InheritedFlags().Lookup("script").Value.String(),
45-
0,
46-
cmd.InheritedFlags().Lookup("tag-prefix").Value.String(),
52+
serviceID,
53+
"", // script is not needed for cleanup
54+
0, // interval is not needed for cleanup
55+
tagPrefix,
56+
logger,
4757
)
58+
59+
logger.Info("Starting tag cleanup", "serviceID", serviceID, "tagPrefix", tagPrefix)
60+
4861
err = t.CleanupTags()
4962
if err != nil {
50-
fmt.Printf("error cleaning up tags: %s", err.Error())
63+
logger.Error("Failed to clean up tags", "error", err)
5164
os.Exit(1)
5265
}
66+
67+
logger.Info("Tag cleanup completed successfully")
5368
},
5469
}
5570

cmd/run.go

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ package cmd
1717

1818
import (
1919
"context"
20-
"fmt"
20+
"log/slog"
2121
"os"
22+
"os/signal"
23+
"syscall"
2224
"time"
2325

2426
"github.com/hashicorp/consul/api"
2527
"github.com/ncode/tagit/pkg/tagit"
26-
2728
"github.com/spf13/cobra"
2829
)
2930

@@ -36,37 +37,93 @@ var runCmd = &cobra.Command{
3637
example: tagit run -s my-super-service -x '/tmp/tag-role.sh'
3738
`,
3839
Run: func(cmd *cobra.Command, args []string) {
39-
interval := cmd.InheritedFlags().Lookup("interval").Value.String()
40-
ctx := context.Background()
40+
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
41+
Level: slog.LevelInfo,
42+
}))
43+
44+
interval, err := cmd.InheritedFlags().GetString("interval")
45+
if err != nil {
46+
logger.Error("Failed to get interval flag", "error", err)
47+
os.Exit(1)
48+
}
49+
4150
if interval == "" || interval == "0" {
42-
fmt.Println("interval is required")
51+
logger.Error("Interval is required")
4352
os.Exit(1)
4453
}
4554

4655
validInterval, err := time.ParseDuration(interval)
4756
if err != nil {
48-
fmt.Printf("invalid interval %s: %s", interval, err.Error())
57+
logger.Error("Invalid interval", "interval", interval, "error", err)
4958
os.Exit(1)
5059
}
5160

5261
config := api.DefaultConfig()
53-
config.Address = cmd.InheritedFlags().Lookup("consul-addr").Value.String()
54-
config.Token = cmd.InheritedFlags().Lookup("token").Value.String()
62+
config.Address, err = cmd.InheritedFlags().GetString("consul-addr")
63+
if err != nil {
64+
logger.Error("Failed to get consul-addr flag", "error", err)
65+
os.Exit(1)
66+
}
67+
config.Token, err = cmd.InheritedFlags().GetString("token")
68+
if err != nil {
69+
logger.Error("Failed to get token flag", "error", err)
70+
os.Exit(1)
71+
}
72+
5573
consulClient, err := api.NewClient(config)
5674
if err != nil {
57-
fmt.Printf("error creating consul client: %s", err.Error())
75+
logger.Error("Failed to create Consul client", "error", err)
76+
os.Exit(1)
77+
}
78+
79+
serviceID, err := cmd.InheritedFlags().GetString("service-id")
80+
if err != nil {
81+
logger.Error("Failed to get service-id flag", "error", err)
82+
os.Exit(1)
83+
}
84+
script, err := cmd.InheritedFlags().GetString("script")
85+
if err != nil {
86+
logger.Error("Failed to get script flag", "error", err)
87+
os.Exit(1)
88+
}
89+
tagPrefix, err := cmd.InheritedFlags().GetString("tag-prefix")
90+
if err != nil {
91+
logger.Error("Failed to get tag-prefix flag", "error", err)
5892
os.Exit(1)
5993
}
6094

6195
t := tagit.New(
6296
tagit.NewConsulAPIWrapper(consulClient),
6397
&tagit.CmdExecutor{},
64-
cmd.InheritedFlags().Lookup("service-id").Value.String(),
65-
cmd.InheritedFlags().Lookup("script").Value.String(),
98+
serviceID,
99+
script,
66100
validInterval,
67-
cmd.InheritedFlags().Lookup("tag-prefix").Value.String(),
101+
tagPrefix,
102+
logger,
68103
)
104+
105+
ctx, cancel := context.WithCancel(context.Background())
106+
defer cancel()
107+
108+
// Setup signal handling for graceful shutdown
109+
sigCh := make(chan os.Signal, 1)
110+
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
111+
112+
go func() {
113+
sig := <-sigCh
114+
logger.Info("Received signal, shutting down", "signal", sig)
115+
cancel()
116+
}()
117+
118+
logger.Info("Starting tagit",
119+
"serviceID", serviceID,
120+
"script", script,
121+
"interval", validInterval,
122+
"tagPrefix", tagPrefix)
123+
69124
t.Run(ctx)
125+
126+
logger.Info("Tagit has stopped")
70127
},
71128
}
72129

0 commit comments

Comments
 (0)