|
|
@ -20,39 +20,27 @@ import (
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
|
|
"github.com/tailscale/hujson"
|
|
|
|
"github.com/tailscale/hujson"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
var (
|
|
|
|
policyFname = flag.String("policy-file", "./policy.hujson", "filename for policy file")
|
|
|
|
rootFlagSet = flag.NewFlagSet("gitops-pusher", flag.ExitOnError)
|
|
|
|
timeout = flag.Duration("timeout", 5*time.Minute, "timeout for the entire CI run")
|
|
|
|
policyFname = rootFlagSet.String("policy-file", "./policy.hujson", "filename for policy file")
|
|
|
|
githubSyntax = flag.Bool("github-syntax", true, "use GitHub Action error syntax (https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message)")
|
|
|
|
timeout = rootFlagSet.Duration("timeout", 5*time.Minute, "timeout for the entire CI run")
|
|
|
|
|
|
|
|
githubSyntax = rootFlagSet.Bool("github-syntax", true, "use GitHub Action error syntax (https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message)")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
func apply(tailnet, apiKey string) func(context.Context, []string) error {
|
|
|
|
flag.Parse()
|
|
|
|
return func(ctx context.Context, args []string) error {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
|
|
|
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tailnet, ok := os.LookupEnv("TS_TAILNET")
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
log.Fatal("set envvar TS_TAILNET to your tailnet's name")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
apiKey, ok := os.LookupEnv("TS_API_KEY")
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
log.Fatal("set envvar TS_API_KEY to your Tailscale API key")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch flag.Arg(0) {
|
|
|
|
|
|
|
|
case "apply":
|
|
|
|
|
|
|
|
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
|
|
|
|
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
localEtag, err := sumFile(*policyFname)
|
|
|
|
localEtag, err := sumFile(*policyFname)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log.Printf("control: %s", controlEtag)
|
|
|
|
log.Printf("control: %s", controlEtag)
|
|
|
@ -60,23 +48,27 @@ func main() {
|
|
|
|
|
|
|
|
|
|
|
|
if controlEtag == localEtag {
|
|
|
|
if controlEtag == localEtag {
|
|
|
|
log.Println("no update needed, doing nothing")
|
|
|
|
log.Println("no update needed, doing nothing")
|
|
|
|
os.Exit(0)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := applyNewACL(ctx, tailnet, apiKey, *policyFname, controlEtag); err != nil {
|
|
|
|
if err := applyNewACL(ctx, tailnet, apiKey, *policyFname, controlEtag); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return err
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case "test":
|
|
|
|
func test(tailnet, apiKey string) func(context.Context, []string) error {
|
|
|
|
|
|
|
|
return func(ctx context.Context, args []string) error {
|
|
|
|
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
|
|
|
|
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
localEtag, err := sumFile(*policyFname)
|
|
|
|
localEtag, err := sumFile(*policyFname)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log.Printf("control: %s", controlEtag)
|
|
|
|
log.Printf("control: %s", controlEtag)
|
|
|
@ -84,16 +76,87 @@ func main() {
|
|
|
|
|
|
|
|
|
|
|
|
if controlEtag == localEtag {
|
|
|
|
if controlEtag == localEtag {
|
|
|
|
log.Println("no updates found, doing nothing")
|
|
|
|
log.Println("no updates found, doing nothing")
|
|
|
|
os.Exit(0)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := testNewACLs(ctx, tailnet, apiKey, *policyFname); err != nil {
|
|
|
|
if err := testNewACLs(ctx, tailnet, apiKey, *policyFname); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getChecksums(tailnet, apiKey string) func(context.Context, []string) error {
|
|
|
|
|
|
|
|
return func(ctx context.Context, args []string) error {
|
|
|
|
|
|
|
|
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localEtag, err := sumFile(*policyFname)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.Printf("control: %s", controlEtag)
|
|
|
|
|
|
|
|
log.Printf("local: %s", localEtag)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
|
|
tailnet, ok := os.LookupEnv("TS_TAILNET")
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
log.Fatal("set envvar TS_TAILNET to your tailnet's name")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
apiKey, ok := os.LookupEnv("TS_API_KEY")
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
log.Fatal("set envvar TS_API_KEY to your Tailscale API key")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
applyCmd := &ffcli.Command{
|
|
|
|
|
|
|
|
Name: "apply",
|
|
|
|
|
|
|
|
ShortUsage: "gitops-pusher [options] apply",
|
|
|
|
|
|
|
|
ShortHelp: "Pushes changes to CONTROL",
|
|
|
|
|
|
|
|
LongHelp: `Pushes changes to CONTROL`,
|
|
|
|
|
|
|
|
Exec: apply(tailnet, apiKey),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
testCmd := &ffcli.Command{
|
|
|
|
|
|
|
|
Name: "test",
|
|
|
|
|
|
|
|
ShortUsage: "gitops-pusher [options] test",
|
|
|
|
|
|
|
|
ShortHelp: "Tests ACL changes",
|
|
|
|
|
|
|
|
LongHelp: "Tests ACL changes",
|
|
|
|
|
|
|
|
Exec: test(tailnet, apiKey),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cksumCmd := &ffcli.Command{
|
|
|
|
|
|
|
|
Name: "checksum",
|
|
|
|
|
|
|
|
ShortUsage: "Shows checksums of ACL files",
|
|
|
|
|
|
|
|
ShortHelp: "Fetch checksum of CONTROL's ACL and the local ACL for comparison",
|
|
|
|
|
|
|
|
LongHelp: "Fetch checksum of CONTROL's ACL and the local ACL for comparison",
|
|
|
|
|
|
|
|
Exec: getChecksums(tailnet, apiKey),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
root := &ffcli.Command{
|
|
|
|
|
|
|
|
ShortUsage: "gitops-pusher [options] <command>",
|
|
|
|
|
|
|
|
ShortHelp: "Push Tailscale ACLs to CONTROL using a GitOps workflow",
|
|
|
|
|
|
|
|
Subcommands: []*ffcli.Command{applyCmd, cksumCmd, testCmd},
|
|
|
|
|
|
|
|
FlagSet: rootFlagSet,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err := root.Parse(os.Args[1:]); err != nil {
|
|
|
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
|
|
|
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err := root.Run(ctx); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
fmt.Println(err)
|
|
|
|
os.Exit(1)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
|
|
|
|
log.Fatalf("usage: %s [options] <test|apply>", os.Args[0])
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func sumFile(fname string) (string, error) {
|
|
|
|
func sumFile(fname string) (string, error) {
|
|
|
|