Skip to content

Commit 5182aca

Browse files
authored
Merge pull request #11 from ncode/juliano/systemd_and_cleanup
Draft: adds systemd and cleanup commands
2 parents ecefa11 + b6c0d69 commit 5182aca

File tree

5 files changed

+243
-26
lines changed

5 files changed

+243
-26
lines changed

cmd/root.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,15 @@ func Execute() {
4141

4242
func init() {
4343
cobra.OnInitialize(initConfig)
44-
runCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tagit.yaml)")
44+
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tagit.yaml)")
45+
rootCmd.PersistentFlags().StringP("consul-addr", "c", "127.0.0.1:8500", "consul address")
46+
rootCmd.PersistentFlags().StringP("service-id", "s", "", "consul service id")
47+
rootCmd.MarkPersistentFlagRequired("service-id")
48+
rootCmd.PersistentFlags().StringP("script", "x", "", "path to script used to generate tags")
49+
rootCmd.MarkPersistentFlagRequired("script")
50+
rootCmd.PersistentFlags().StringP("tag-prefix", "p", "tagged", "prefix to be added to tags")
51+
rootCmd.PersistentFlags().StringP("interval", "i", "60s", "interval to run the script")
52+
rootCmd.PersistentFlags().StringP("token", "t", "", "consul token")
4553
}
4654

4755
// initConfig reads in config file and ENV variables if set.

cmd/run.go

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,8 @@ var runCmd = &cobra.Command{
3636
example: tagit run -s my-super-service -x '/tmp/tag-role.sh'
3737
`,
3838
Run: func(cmd *cobra.Command, args []string) {
39-
serviceID := cmd.PersistentFlags().Lookup("service-id").Value.String()
40-
script := cmd.PersistentFlags().Lookup("script").Value.String()
41-
tagPrefix := cmd.PersistentFlags().Lookup("tag-prefix").Value.String()
42-
interval := cmd.PersistentFlags().Lookup("interval").Value.String()
39+
interval := cmd.InheritedFlags().Lookup("interval").Value.String()
4340
ctx := context.Background()
44-
45-
if serviceID == "" {
46-
fmt.Println("service-id is required")
47-
os.Exit(1)
48-
}
49-
50-
if script == "" {
51-
fmt.Println("script is required")
52-
os.Exit(1)
53-
}
54-
5541
if interval == "" || interval == "0" {
5642
fmt.Println("interval is required")
5743
os.Exit(1)
@@ -64,26 +50,26 @@ example: tagit run -s my-super-service -x '/tmp/tag-role.sh'
6450
}
6551

6652
config := api.DefaultConfig()
67-
config.Address = cmd.PersistentFlags().Lookup("consul-addr").Value.String()
68-
config.Token = cmd.PersistentFlags().Lookup("token").Value.String()
53+
config.Address = cmd.InheritedFlags().Lookup("consul-addr").Value.String()
54+
config.Token = cmd.InheritedFlags().Lookup("token").Value.String()
6955
consulClient, err := api.NewClient(config)
7056
if err != nil {
7157
fmt.Printf("error creating consul client: %s", err.Error())
7258
os.Exit(1)
7359
}
7460

75-
t := tagit.New(tagit.NewConsulAPIWrapper(consulClient), &tagit.CmdExecutor{}, serviceID, script, validInterval, tagPrefix)
61+
t := tagit.New(
62+
tagit.NewConsulAPIWrapper(consulClient),
63+
&tagit.CmdExecutor{},
64+
cmd.InheritedFlags().Lookup("service-id").Value.String(),
65+
cmd.InheritedFlags().Lookup("script").Value.String(),
66+
validInterval,
67+
cmd.InheritedFlags().Lookup("tag-prefix").Value.String(),
68+
)
7669
t.Run(ctx)
7770
},
7871
}
7972

8073
func init() {
8174
rootCmd.AddCommand(runCmd)
82-
83-
runCmd.PersistentFlags().StringP("consul-addr", "c", "127.0.0.1:8500", "consul address")
84-
runCmd.PersistentFlags().StringP("service-id", "s", "", "consul service id")
85-
runCmd.PersistentFlags().StringP("script", "x", "", "path to script used to generate tags")
86-
runCmd.PersistentFlags().StringP("tag-prefix", "p", "tagged", "prefix to be added to tags")
87-
runCmd.PersistentFlags().StringP("interval", "i", "60s", "interval to run the script")
88-
runCmd.PersistentFlags().StringP("token", "t", "", "consul token")
8975
}

cmd/systemd.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright © 2024 Juliano Martinez <[email protected]>
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
"github.com/ncode/tagit/pkg/systemd"
21+
"github.com/spf13/cobra"
22+
)
23+
24+
// systemdCmd represents the systemd command
25+
var systemdCmd = &cobra.Command{
26+
Use: "systemd",
27+
Short: "A brief description of your command",
28+
Long: `A longer description that spans multiple lines and likely contains examples
29+
and usage of using your command. For example:
30+
31+
Cobra is a CLI library for Go that empowers applications.
32+
This application is a tool to generate the needed files
33+
to quickly create a Cobra application.`,
34+
Run: func(cmd *cobra.Command, args []string) {
35+
fields := &systemd.Fields{
36+
User: cmd.PersistentFlags().Lookup("user").Value.String(),
37+
Group: cmd.PersistentFlags().Lookup("group").Value.String(),
38+
ConsulAddr: cmd.InheritedFlags().Lookup("consul-addr").Value.String(),
39+
ServiceID: cmd.InheritedFlags().Lookup("service-id").Value.String(),
40+
Script: cmd.InheritedFlags().Lookup("script").Value.String(),
41+
TagPrefix: cmd.InheritedFlags().Lookup("tag-prefix").Value.String(),
42+
Interval: cmd.InheritedFlags().Lookup("interval").Value.String(),
43+
Token: cmd.InheritedFlags().Lookup("token").Value.String(),
44+
}
45+
fmt.Println(systemd.RenderTemplate(fields))
46+
},
47+
}
48+
49+
func init() {
50+
rootCmd.AddCommand(systemdCmd)
51+
systemdCmd.PersistentFlags().StringP("user", "u", "nobody", "user to run the service")
52+
systemdCmd.PersistentFlags().StringP("group", "g", "nobody", "group to run the service")
53+
}

pkg/systemd/systemd.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package systemd
2+
3+
import (
4+
"bytes"
5+
"text/template"
6+
)
7+
8+
// Fields is the struct that holds the fields for the systemd service.
9+
type Fields struct {
10+
ServiceID string
11+
Script string
12+
TagPrefix string
13+
Interval string
14+
Token string
15+
ConsulAddr string
16+
User string
17+
Group string
18+
}
19+
20+
// serviceTemplate is the template for the systemd service.
21+
var serviceTemplate = `
22+
[Unit]
23+
Description=Tagit {{ .ServiceID }}
24+
After=network.target
25+
After=network-online.target
26+
Wants=network-online.target
27+
28+
[Service]
29+
Type=simple
30+
ExecStart=/usr/bin/tagit run -s {{ .ServiceID }} -x {{ .Script }} -p {{ .TagPrefix }} -i {{ .Interval }}{{- if .Token }} -t {{ .Token }}{{- end }}{{ if .ConsulAddr }} -c {{ .ConsulAddr }}{{- end }}
31+
Environment=HOME=/var/run/taggit/{{ .ServiceID }}
32+
Restart=always
33+
User={{ .User }}
34+
Group={{ .Group }}
35+
36+
[Install]
37+
WantedBy=multi-user.target
38+
`
39+
40+
// RenderTemplate renders the template for the systemd service.
41+
func RenderTemplate(fields *Fields) (string, error) {
42+
tmpl, err := template.New("serviceTemplate").Parse(serviceTemplate)
43+
if err != nil {
44+
return "", err
45+
}
46+
47+
var tmplBuffer bytes.Buffer
48+
err = tmpl.Execute(&tmplBuffer, fields)
49+
if err != nil {
50+
return "", err
51+
}
52+
53+
return tmplBuffer.String(), nil
54+
}

pkg/systemd/systemd_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package systemd
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestRenderTemplate(t *testing.T) {
9+
// Define a test case struct
10+
type testCase struct {
11+
name string
12+
fields Fields
13+
wantErr bool
14+
checkStr string // A substring we expect in the output
15+
expectStr bool // A flag to indicate the absence of a substring
16+
}
17+
18+
// Define your test cases
19+
testCases := []testCase{
20+
{
21+
name: "Basic test",
22+
fields: Fields{
23+
ServiceID: "testservice",
24+
Script: "testscript",
25+
TagPrefix: "testprefix",
26+
Interval: "testinterval",
27+
Token: "testtoken",
28+
ConsulAddr: "testaddr",
29+
User: "testuser",
30+
Group: "testgroup",
31+
},
32+
wantErr: false,
33+
checkStr: "ExecStart=/usr/bin/tagit run -s testservice -x testscript",
34+
expectStr: true,
35+
},
36+
{
37+
name: "With Token",
38+
fields: Fields{
39+
ServiceID: "testservice",
40+
Token: "sometoken",
41+
},
42+
wantErr: false,
43+
checkStr: "-t sometoken",
44+
expectStr: true,
45+
},
46+
{
47+
name: "Without Token",
48+
fields: Fields{
49+
ServiceID: "testservice",
50+
},
51+
wantErr: false,
52+
checkStr: "-t",
53+
expectStr: false,
54+
},
55+
{
56+
name: "With Consul Address",
57+
fields: Fields{
58+
ServiceID: "testservice",
59+
ConsulAddr: "someaddress",
60+
},
61+
wantErr: false,
62+
checkStr: "-c someaddress",
63+
expectStr: true,
64+
},
65+
{
66+
name: "Without Consul Address",
67+
fields: Fields{
68+
ServiceID: "testservice",
69+
},
70+
wantErr: false,
71+
checkStr: "-c someaddress",
72+
expectStr: false,
73+
},
74+
{
75+
name: "Empty Fields",
76+
fields: Fields{},
77+
wantErr: false,
78+
checkStr: "ExecStart=/usr/bin/tagit run -s",
79+
expectStr: true,
80+
},
81+
{
82+
name: "Consul Address Only",
83+
fields: Fields{
84+
ConsulAddr: "127.0.0.1",
85+
},
86+
wantErr: false,
87+
checkStr: "-c 127.0.0.1",
88+
expectStr: true,
89+
},
90+
}
91+
92+
// Iterate over the test cases
93+
for _, tc := range testCases {
94+
t.Run(tc.name, func(t *testing.T) {
95+
got, err := RenderTemplate(&tc.fields)
96+
if (err != nil) != tc.wantErr {
97+
t.Errorf("RenderTemplate() error = %v, wantErr %v", err, tc.wantErr)
98+
return
99+
}
100+
101+
// Check for the presence or absence of the token
102+
if tc.expectStr && !strings.Contains(got, tc.checkStr) {
103+
t.Errorf("RenderTemplate() = %v, want %v", got, tc.checkStr)
104+
} else if !tc.expectStr && strings.Contains(got, tc.checkStr) {
105+
t.Errorf("RenderTemplate() = %v, should not contain %v", got, tc.checkStr)
106+
}
107+
})
108+
}
109+
}
110+
111+
func TestRenderTemplateFailure(t *testing.T) {
112+
_, err := RenderTemplate(nil)
113+
if err == nil {
114+
t.Errorf("RenderTemplate() with nil input did not fail as expected")
115+
}
116+
}

0 commit comments

Comments
 (0)