mirror of
https://github.com/fumiama/terasu-cloudflared.git
synced 2026-06-05 00:50:24 +08:00
Using path package methods can cause errors on windows machines. path methods are used for url operations and unix specific operation. filepath methods are used for file system paths and its cross platform. Remove strings.HasSuffix and use filepath.Ext and path.Ext for file and url extenstions respectively.
223 lines
5.3 KiB
Go
223 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"text/template"
|
|
|
|
homedir "github.com/mitchellh/go-homedir"
|
|
|
|
"github.com/cloudflare/cloudflared/config"
|
|
)
|
|
|
|
type ServiceTemplate struct {
|
|
Path string
|
|
Content string
|
|
FileMode os.FileMode
|
|
}
|
|
|
|
type ServiceTemplateArgs struct {
|
|
Path string
|
|
ExtraArgs []string
|
|
}
|
|
|
|
func (st *ServiceTemplate) ResolvePath() (string, error) {
|
|
resolvedPath, err := homedir.Expand(st.Path)
|
|
if err != nil {
|
|
return "", fmt.Errorf("error resolving path %s: %v", st.Path, err)
|
|
}
|
|
return resolvedPath, nil
|
|
}
|
|
|
|
func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error {
|
|
tmpl, err := template.New(st.Path).Parse(st.Content)
|
|
if err != nil {
|
|
return fmt.Errorf("error generating %s template: %v", st.Path, err)
|
|
}
|
|
resolvedPath, err := st.ResolvePath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = os.Stat(resolvedPath); err == nil {
|
|
return fmt.Errorf(serviceAlreadyExistsWarn(resolvedPath))
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
err = tmpl.Execute(&buffer, args)
|
|
if err != nil {
|
|
return fmt.Errorf("error generating %s: %v", st.Path, err)
|
|
}
|
|
fileMode := os.FileMode(0o644)
|
|
if st.FileMode != 0 {
|
|
fileMode = st.FileMode
|
|
}
|
|
|
|
plistFolder := filepath.Dir(resolvedPath)
|
|
err = os.MkdirAll(plistFolder, 0o755)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating %s: %v", plistFolder, err)
|
|
}
|
|
|
|
err = os.WriteFile(resolvedPath, buffer.Bytes(), fileMode)
|
|
if err != nil {
|
|
return fmt.Errorf("error writing %s: %v", resolvedPath, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (st *ServiceTemplate) Remove() error {
|
|
resolvedPath, err := st.ResolvePath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.Remove(resolvedPath)
|
|
if err != nil {
|
|
return fmt.Errorf("error deleting %s: %v", resolvedPath, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func serviceAlreadyExistsWarn(service string) string {
|
|
return fmt.Sprintf("cloudflared service is already installed at %s; if you are running a cloudflared tunnel, you "+
|
|
"can point it to multiple origins, avoiding the need to run more than one cloudflared service in the "+
|
|
"same machine; otherwise if you are really sure, you can do `cloudflared service uninstall` to clean "+
|
|
"up the existing service and then try again this command",
|
|
service,
|
|
)
|
|
}
|
|
|
|
func runCommand(command string, args ...string) error {
|
|
cmd := exec.Command(command, args...)
|
|
stderr, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
return fmt.Errorf("error getting stderr pipe: %v", err)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("error starting %s: %v", command, err)
|
|
}
|
|
|
|
output, _ := io.ReadAll(stderr)
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
return fmt.Errorf("%s %v returned with error code %v due to: %v", command, args, err, string(output))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ensureConfigDirExists(configDir string) error {
|
|
ok, err := config.FileExists(configDir)
|
|
if !ok && err == nil {
|
|
err = os.Mkdir(configDir, 0755)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// openFile opens the file at path. If create is set and the file exists, returns nil, true, nil
|
|
func openFile(path string, create bool) (file *os.File, exists bool, err error) {
|
|
expandedPath, err := homedir.Expand(path)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
if create {
|
|
fileInfo, err := os.Stat(expandedPath)
|
|
if err == nil && fileInfo.Size() > 0 {
|
|
return nil, true, nil
|
|
}
|
|
file, err = os.OpenFile(expandedPath, os.O_RDWR|os.O_CREATE, 0600)
|
|
} else {
|
|
file, err = os.Open(expandedPath)
|
|
}
|
|
return file, false, err
|
|
}
|
|
|
|
func copyCredential(srcCredentialPath, destCredentialPath string) error {
|
|
destFile, exists, err := openFile(destCredentialPath, true)
|
|
if err != nil {
|
|
return err
|
|
} else if exists {
|
|
// credentials already exist, do nothing
|
|
return nil
|
|
}
|
|
defer destFile.Close()
|
|
|
|
srcFile, _, err := openFile(srcCredentialPath, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer srcFile.Close()
|
|
|
|
// Copy certificate
|
|
_, err = io.Copy(destFile, srcFile)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to copy %s to %s: %v", srcCredentialPath, destCredentialPath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func copyFile(src, dest string) error {
|
|
srcFile, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer srcFile.Close()
|
|
|
|
destFile, err := os.Create(dest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ok := false
|
|
defer func() {
|
|
destFile.Close()
|
|
if !ok {
|
|
_ = os.Remove(dest)
|
|
}
|
|
}()
|
|
|
|
if _, err := io.Copy(destFile, srcFile); err != nil {
|
|
return err
|
|
}
|
|
|
|
ok = true
|
|
return nil
|
|
}
|
|
|
|
func copyConfig(srcConfigPath, destConfigPath string) error {
|
|
// Copy or create config
|
|
destFile, exists, err := openFile(destConfigPath, true)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot open %s with error: %s", destConfigPath, err)
|
|
} else if exists {
|
|
// config already exists, do nothing
|
|
return nil
|
|
}
|
|
defer destFile.Close()
|
|
|
|
srcFile, _, err := openFile(srcConfigPath, false)
|
|
if err != nil {
|
|
fmt.Println("Your service needs a config file that at least specifies the hostname option.")
|
|
fmt.Println("Type in a hostname now, or leave it blank and create the config file later.")
|
|
fmt.Print("Hostname: ")
|
|
reader := bufio.NewReader(os.Stdin)
|
|
input, _ := reader.ReadString('\n')
|
|
if input == "" {
|
|
return err
|
|
}
|
|
fmt.Fprintf(destFile, "hostname: %s\n", input)
|
|
} else {
|
|
defer srcFile.Close()
|
|
_, err = io.Copy(destFile, srcFile)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|