main
1//go:build mage
2// +build mage
3
4package main
5
6import (
7 "context"
8 "encoding/json"
9 "io/ioutil"
10 "os"
11 "path/filepath"
12 "strings"
13
14 "github.com/magefile/mage/mg"
15 "github.com/magefile/mage/sh"
16 "github.com/magefile/mage/target"
17 "github.com/xlgmokha/x/pkg/env"
18 "github.com/xlgmokha/x/pkg/x"
19)
20
21type Step mg.Namespace
22
23func (s Step) Clean() error {
24 globs := []string{
25 "tmp/step/*/*",
26 }
27 for _, item := range globs {
28 fs, err := filepath.Glob(item)
29 if err != nil {
30 return err
31 }
32 for _, f := range fs {
33 if strings.HasSuffix(f, "/.keep") {
34 continue
35 }
36 if err := os.RemoveAll(f); err != nil {
37 return err
38 }
39 }
40 }
41 return nil
42}
43
44func (s Step) Setup() {
45 mg.SerialDeps(s.mkPassword, s.createCA, s.enableACMEProvisioner)
46}
47
48func (s Step) Install() error {
49 return sh.RunWithV(
50 s.env(),
51 "step",
52 "certificate",
53 "install",
54 s.pathPlus("/certs/root_ca.crt"),
55 )
56}
57
58func (s Step) Server(ctx context.Context) error {
59 mg.SerialDeps(s.Setup)
60
61 return sh.RunWithV(
62 s.env(),
63 "step-ca",
64 s.pathPlus("config/ca.json"),
65 "--password-file="+s.pathPlus("password.txt"),
66 )
67}
68
69func (s Step) Provisioners() error {
70 return sh.RunV("curl", "-k", "-s", "https://localhost:8081/provisioners")
71}
72
73func (s Step) ACME() error {
74 return sh.RunV("curl", "-k", "-s", "https://localhost:8081/acme/acme/directory")
75}
76
77func (s Step) Status() {
78 mg.SerialDeps(s.Provisioners, s.ACME)
79}
80
81func (s Step) mkPassword() error {
82 file := s.passwordFile()
83 if ok, err := target.Dir(file); err != nil || !ok {
84 return nil
85 }
86
87 return os.WriteFile(file, []byte("password"), 0600)
88}
89
90func (s Step) createCA() error {
91 if ok, err := target.Dir(s.pathPlus("config/ca.json"), s.passwordFile()); err != nil || !ok {
92 return nil
93 }
94
95 return sh.RunWithV(
96 s.env(),
97 "step",
98 "ca",
99 "init",
100 "--deployment-type=standalone",
101 "--address=localhost:8081",
102 "--dns=localhost",
103 "--dns=*.localhost",
104 "--name=CA",
105 "--provisioner=example",
106 "--provisioner-password-file="+s.passwordFile(),
107 "--password-file="+s.passwordFile(),
108 )
109}
110
111func (s Step) enableACMEProvisioner() error {
112 bytes, err := ioutil.ReadFile(s.pathPlus("config/ca.json"))
113 if err != nil {
114 return err
115 }
116
117 items := map[string]interface{}{}
118 if err := json.Unmarshal(bytes, &items); err != nil {
119 return err
120 }
121
122 provisioners := items["authority"].(map[string]interface{})["provisioners"].([]interface{})
123 if len(provisioners) < 2 {
124 return sh.RunWithV(s.env(), "step", "ca", "provisioner", "add", "acme", "--type", "ACME")
125 }
126 return nil
127}
128
129func (step Step) passwordFile() string {
130 return step.pathPlus("password.txt")
131}
132
133func (s Step) path() string {
134 return env.Fetch("STEPPATH", filepath.Join(x.Must(os.Getwd()), "/tmp/step"))
135}
136
137func (s Step) env() map[string]string {
138 return map[string]string{
139 "STEPPATH": s.path(),
140 "HOST": "localhost",
141 "PORT": "8081",
142 }
143}
144
145func (s Step) pathPlus(path string) string {
146 return filepath.Join(s.path(), path)
147}