mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-28 21:58:15 +02:00
Hybrid install mode done?
This commit is contained in:
parent
ad8ab63fd5
commit
8273554a1c
4 changed files with 140 additions and 100 deletions
|
@ -1,6 +1,15 @@
|
||||||
# To see all available options, please visit the docs:
|
# To see all available options, please visit the docs:
|
||||||
# https://docs.digpangolin.com/self-host/advanced/config-file
|
# https://docs.digpangolin.com/self-host/advanced/config-file
|
||||||
|
|
||||||
|
gerbil:
|
||||||
|
start_port: 51820
|
||||||
|
base_endpoint: "{{.DashboardDomain}}"
|
||||||
|
{{if .HybridMode}}
|
||||||
|
hybrid:
|
||||||
|
id: "{{.HybridId}}"
|
||||||
|
secret: "{{.HybridSecret}}"
|
||||||
|
|
||||||
|
{{else}}
|
||||||
app:
|
app:
|
||||||
dashboard_url: "https://{{.DashboardDomain}}"
|
dashboard_url: "https://{{.DashboardDomain}}"
|
||||||
log_level: "info"
|
log_level: "info"
|
||||||
|
@ -17,11 +26,6 @@ server:
|
||||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||||
credentials: false
|
credentials: false
|
||||||
|
|
||||||
gerbil:
|
|
||||||
start_port: 51820
|
|
||||||
base_endpoint: "{{.DashboardDomain}}"
|
|
||||||
|
|
||||||
{{if .EnableEmail}}
|
{{if .EnableEmail}}
|
||||||
email:
|
email:
|
||||||
smtp_host: "{{.EmailSMTPHost}}"
|
smtp_host: "{{.EmailSMTPHost}}"
|
||||||
|
@ -30,15 +34,9 @@ email:
|
||||||
smtp_pass: "{{.EmailSMTPPass}}"
|
smtp_pass: "{{.EmailSMTPPass}}"
|
||||||
no_reply: "{{.EmailNoReply}}"
|
no_reply: "{{.EmailNoReply}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
require_email_verification: {{.EnableEmail}}
|
require_email_verification: {{.EnableEmail}}
|
||||||
disable_signup_without_invite: true
|
disable_signup_without_invite: true
|
||||||
disable_user_create_org: false
|
disable_user_create_org: false
|
||||||
allow_raw_resources: true
|
allow_raw_resources: true
|
||||||
|
|
||||||
{{if and .HybridMode .HybridId .HybridSecret}}
|
|
||||||
hybrid:
|
|
||||||
id: "{{.HybridId}}"
|
|
||||||
secret: "{{.HybridSecret}}"
|
|
||||||
{{end}}
|
{{end}}
|
|
@ -6,6 +6,8 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
|
- pangolin-data:/var/certificates
|
||||||
|
- pangolin-data:/var/dynamic
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||||
interval: "10s"
|
interval: "10s"
|
||||||
|
@ -31,8 +33,8 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 51820:51820/udp
|
- 51820:51820/udp
|
||||||
- 21820:21820/udp
|
- 21820:21820/udp
|
||||||
- 443:443 # Port for traefik because of the network_mode
|
- 443:{{if .HybridMode}}8443{{else}}443{{end}}
|
||||||
- 80:80 # Port for traefik because of the network_mode
|
- 80:80
|
||||||
{{end}}
|
{{end}}
|
||||||
traefik:
|
traefik:
|
||||||
image: docker.io/traefik:v3.5
|
image: docker.io/traefik:v3.5
|
||||||
|
@ -54,9 +56,15 @@ services:
|
||||||
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
|
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
|
||||||
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
||||||
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
|
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
|
||||||
|
# Shared volume for certificates and dynamic config in file mode
|
||||||
|
- pangolin-data:/var/certificates:ro
|
||||||
|
- pangolin-data:/var/dynamic:ro
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
name: pangolin
|
name: pangolin
|
||||||
{{if .EnableIPv6}} enable_ipv6: true{{end}}
|
{{if .EnableIPv6}} enable_ipv6: true{{end}}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pangolin-data:
|
||||||
|
|
|
@ -3,12 +3,17 @@ api:
|
||||||
dashboard: true
|
dashboard: true
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
|
{{if not .HybridMode}}
|
||||||
http:
|
http:
|
||||||
endpoint: "http://pangolin:3001/api/v1/traefik-config"
|
endpoint: "http://pangolin:3001/api/v1/traefik-config"
|
||||||
pollInterval: "5s"
|
pollInterval: "5s"
|
||||||
file:
|
file:
|
||||||
filename: "/etc/traefik/dynamic_config.yml"
|
filename: "/etc/traefik/dynamic_config.yml"
|
||||||
|
{{else}}
|
||||||
|
file:
|
||||||
|
directory: "/var/dynamic"
|
||||||
|
watch: true
|
||||||
|
{{end}}
|
||||||
experimental:
|
experimental:
|
||||||
plugins:
|
plugins:
|
||||||
badger:
|
badger:
|
||||||
|
@ -22,7 +27,7 @@ log:
|
||||||
maxBackups: 3
|
maxBackups: 3
|
||||||
maxAge: 3
|
maxAge: 3
|
||||||
compress: true
|
compress: true
|
||||||
|
{{if not .HybridMode}}
|
||||||
certificatesResolvers:
|
certificatesResolvers:
|
||||||
letsencrypt:
|
letsencrypt:
|
||||||
acme:
|
acme:
|
||||||
|
@ -31,7 +36,7 @@ certificatesResolvers:
|
||||||
email: "{{.LetsEncryptEmail}}"
|
email: "{{.LetsEncryptEmail}}"
|
||||||
storage: "/letsencrypt/acme.json"
|
storage: "/letsencrypt/acme.json"
|
||||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
{{end}}
|
||||||
entryPoints:
|
entryPoints:
|
||||||
web:
|
web:
|
||||||
address: ":80"
|
address: ":80"
|
||||||
|
@ -40,9 +45,12 @@ entryPoints:
|
||||||
transport:
|
transport:
|
||||||
respondingTimeouts:
|
respondingTimeouts:
|
||||||
readTimeout: "30m"
|
readTimeout: "30m"
|
||||||
http:
|
{{if not .HybridMode}} http:
|
||||||
tls:
|
tls:
|
||||||
certResolver: "letsencrypt"
|
certResolver: "letsencrypt"{{end}}
|
||||||
|
|
||||||
serversTransport:
|
serversTransport:
|
||||||
insecureSkipVerify: true
|
insecureSkipVerify: true
|
||||||
|
|
||||||
|
ping:
|
||||||
|
entryPoint: "web"
|
190
install/main.go
190
install/main.go
|
@ -65,12 +65,9 @@ func main() {
|
||||||
|
|
||||||
fmt.Println("Welcome to the Pangolin installer!")
|
fmt.Println("Welcome to the Pangolin installer!")
|
||||||
fmt.Println("This installer will help you set up Pangolin on your server.")
|
fmt.Println("This installer will help you set up Pangolin on your server.")
|
||||||
fmt.Println("")
|
fmt.Println("\nPlease make sure you have the following prerequisites:")
|
||||||
fmt.Println("Please make sure you have the following prerequisites:")
|
|
||||||
fmt.Println("- Open TCP ports 80 and 443 and UDP ports 51820 and 21820 on your VPS and firewall.")
|
fmt.Println("- Open TCP ports 80 and 443 and UDP ports 51820 and 21820 on your VPS and firewall.")
|
||||||
fmt.Println("")
|
fmt.Println("\nLets get started!")
|
||||||
fmt.Println("Lets get started!")
|
|
||||||
fmt.Println("")
|
|
||||||
|
|
||||||
if os.Geteuid() == 0 { // WE NEED TO BE SUDO TO CHECK THIS
|
if os.Geteuid() == 0 { // WE NEED TO BE SUDO TO CHECK THIS
|
||||||
for _, p := range []int{80, 443} {
|
for _, p := range []int{80, 443} {
|
||||||
|
@ -102,51 +99,52 @@ func main() {
|
||||||
|
|
||||||
moveFile("config/docker-compose.yml", "docker-compose.yml")
|
moveFile("config/docker-compose.yml", "docker-compose.yml")
|
||||||
|
|
||||||
config.InstallationContainerType = podmanOrDocker(reader)
|
fmt.Println("\nConfiguration files created successfully!")
|
||||||
|
|
||||||
if !isDockerInstalled() && runtime.GOOS == "linux" && config.InstallationContainerType == Docker {
|
|
||||||
if readBool(reader, "Docker is not installed. Would you like to install it?", true) {
|
|
||||||
installDocker()
|
|
||||||
// try to start docker service but ignore errors
|
|
||||||
if err := startDockerService(); err != nil {
|
|
||||||
fmt.Println("Error starting Docker service:", err)
|
|
||||||
} else {
|
|
||||||
fmt.Println("Docker service started successfully!")
|
|
||||||
}
|
|
||||||
// wait 10 seconds for docker to start checking if docker is running every 2 seconds
|
|
||||||
fmt.Println("Waiting for Docker to start...")
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
if isDockerRunning() {
|
|
||||||
fmt.Println("Docker is running!")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fmt.Println("Docker is not running yet, waiting...")
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
}
|
|
||||||
if !isDockerRunning() {
|
|
||||||
fmt.Println("Docker is still not running after 10 seconds. Please check the installation.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Println("Docker installed successfully!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("\n=== Starting installation ===")
|
fmt.Println("\n=== Starting installation ===")
|
||||||
|
|
||||||
if (isDockerInstalled() && config.InstallationContainerType == Docker) ||
|
if readBool(reader, "Would you like to install and start the containers?", true) {
|
||||||
(isPodmanInstalled() && config.InstallationContainerType == Podman) {
|
|
||||||
if readBool(reader, "Would you like to install and start the containers?", true) {
|
|
||||||
if err := pullContainers(config.InstallationContainerType); err != nil {
|
|
||||||
fmt.Println("Error: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := startContainers(config.InstallationContainerType); err != nil {
|
config.InstallationContainerType = podmanOrDocker(reader)
|
||||||
fmt.Println("Error: ", err)
|
|
||||||
return
|
if !isDockerInstalled() && runtime.GOOS == "linux" && config.InstallationContainerType == Docker {
|
||||||
|
if readBool(reader, "Docker is not installed. Would you like to install it?", true) {
|
||||||
|
installDocker()
|
||||||
|
// try to start docker service but ignore errors
|
||||||
|
if err := startDockerService(); err != nil {
|
||||||
|
fmt.Println("Error starting Docker service:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Docker service started successfully!")
|
||||||
|
}
|
||||||
|
// wait 10 seconds for docker to start checking if docker is running every 2 seconds
|
||||||
|
fmt.Println("Waiting for Docker to start...")
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if isDockerRunning() {
|
||||||
|
fmt.Println("Docker is running!")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Println("Docker is not running yet, waiting...")
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
if !isDockerRunning() {
|
||||||
|
fmt.Println("Docker is still not running after 10 seconds. Please check the installation.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("Docker installed successfully!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := pullContainers(config.InstallationContainerType); err != nil {
|
||||||
|
fmt.Println("Error: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := startContainers(config.InstallationContainerType); err != nil {
|
||||||
|
fmt.Println("Error: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Looks like you already installed, so I am going to do the setup...")
|
fmt.Println("Looks like you already installed, so I am going to do the setup...")
|
||||||
|
|
||||||
|
@ -171,15 +169,16 @@ func main() {
|
||||||
config = collectUserInput(reader)
|
config = collectUserInput(reader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Pangolin is already installed with hybrid section
|
// Check if Pangolin is already installed with hybrid section
|
||||||
if checkIsPangolinInstalledWithHybrid() {
|
// if checkIsPangolinInstalledWithHybrid() {
|
||||||
fmt.Println("\n=== Convert to Self-Host Node ===")
|
// fmt.Println("\n=== Convert to Self-Host Node ===")
|
||||||
if readBool(reader, "Do you want to convert this Pangolin instance into a manage self-host node?", true) {
|
// if readBool(reader, "Do you want to convert this Pangolin instance into a managed self-host node?", true) {
|
||||||
fmt.Println("hello world")
|
// fmt.Println("hello world")
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !checkIsCrowdsecInstalledInCompose() {
|
if !checkIsCrowdsecInstalledInCompose() {
|
||||||
|
@ -217,25 +216,30 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup Token Section
|
if !config.HybridMode {
|
||||||
fmt.Println("\n=== Setup Token ===")
|
// Setup Token Section
|
||||||
|
fmt.Println("\n=== Setup Token ===")
|
||||||
|
|
||||||
// Check if containers were started during this installation
|
// Check if containers were started during this installation
|
||||||
containersStarted := false
|
containersStarted := false
|
||||||
if (isDockerInstalled() && config.InstallationContainerType == Docker) ||
|
if (isDockerInstalled() && config.InstallationContainerType == Docker) ||
|
||||||
(isPodmanInstalled() && config.InstallationContainerType == Podman) {
|
(isPodmanInstalled() && config.InstallationContainerType == Podman) {
|
||||||
// Try to fetch and display the token if containers are running
|
// Try to fetch and display the token if containers are running
|
||||||
containersStarted = true
|
containersStarted = true
|
||||||
printSetupToken(config.InstallationContainerType, config.DashboardDomain)
|
printSetupToken(config.InstallationContainerType, config.DashboardDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If containers weren't started or token wasn't found, show instructions
|
||||||
|
if !containersStarted {
|
||||||
|
showSetupTokenInstructions(config.InstallationContainerType, config.DashboardDomain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If containers weren't started or token wasn't found, show instructions
|
fmt.Println("\nInstallation complete!")
|
||||||
if !containersStarted {
|
|
||||||
showSetupTokenInstructions(config.InstallationContainerType, config.DashboardDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Installation complete!")
|
if !config.HybridMode {
|
||||||
fmt.Printf("\nTo complete the initial setup, please visit:\nhttps://%s/auth/initial-setup\n", config.DashboardDomain)
|
fmt.Printf("\nTo complete the initial setup, please visit:\nhttps://%s/auth/initial-setup\n", config.DashboardDomain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
|
||||||
|
@ -310,18 +314,32 @@ func collectUserInput(reader *bufio.Reader) Config {
|
||||||
|
|
||||||
// Basic configuration
|
// Basic configuration
|
||||||
fmt.Println("\n=== Basic Configuration ===")
|
fmt.Println("\n=== Basic Configuration ===")
|
||||||
config.HybridMode = readBoolNoDefault(reader, "Do you want to use hybrid mode?")
|
for {
|
||||||
|
response := readString(reader, "Do you want to install Pangolin as a cloud-managed self-hosted node? (yes/no)", "")
|
||||||
|
if strings.EqualFold(response, "yes") || strings.EqualFold(response, "y") {
|
||||||
|
config.HybridMode = true
|
||||||
|
break
|
||||||
|
} else if strings.EqualFold(response, "no") || strings.EqualFold(response, "n") {
|
||||||
|
config.HybridMode = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Println("Please answer 'yes' or 'no'")
|
||||||
|
}
|
||||||
|
|
||||||
if config.HybridMode {
|
if config.HybridMode {
|
||||||
alreadyHaveCreds := readBool(reader, "Do you already have credentials from the dashboard?", false)
|
alreadyHaveCreds := readBool(reader, "Do you already have credentials from the dashboard?", false)
|
||||||
|
|
||||||
if alreadyHaveCreds {
|
if alreadyHaveCreds {
|
||||||
config.HybridId = readString(reader, "Enter your hybrid ID", "")
|
config.HybridId = readString(reader, "Enter your hybrid ID", "")
|
||||||
config.HybridSecret = readString(reader, "Enter your hybrid secret", "")
|
config.HybridSecret = readString(reader, "Enter your hybrid secret", "")
|
||||||
|
} else {
|
||||||
|
// Just print instructions for right now
|
||||||
|
fmt.Println("Please visit https://pangolin.fossorial.io, create a self hosted node, and return with the credentials.")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !config.HybridMode {
|
config.DashboardDomain = readString(reader, "The public addressable IP address for this node or a domain pointing to it", "")
|
||||||
|
config.InstallGerbil = true
|
||||||
|
} else {
|
||||||
config.BaseDomain = readString(reader, "Enter your base domain (no subdomain e.g. example.com)", "")
|
config.BaseDomain = readString(reader, "Enter your base domain (no subdomain e.g. example.com)", "")
|
||||||
|
|
||||||
// Set default dashboard domain after base domain is collected
|
// Set default dashboard domain after base domain is collected
|
||||||
|
@ -331,16 +349,12 @@ func collectUserInput(reader *bufio.Reader) Config {
|
||||||
}
|
}
|
||||||
config.DashboardDomain = readString(reader, "Enter the domain for the Pangolin dashboard", defaultDashboardDomain)
|
config.DashboardDomain = readString(reader, "Enter the domain for the Pangolin dashboard", defaultDashboardDomain)
|
||||||
config.LetsEncryptEmail = readString(reader, "Enter email for Let's Encrypt certificates", "")
|
config.LetsEncryptEmail = readString(reader, "Enter email for Let's Encrypt certificates", "")
|
||||||
}
|
config.InstallGerbil = readBool(reader, "Do you want to use Gerbil to allow tunneled connections", true)
|
||||||
|
|
||||||
config.InstallGerbil = readBool(reader, "Do you want to use Gerbil to allow tunneled connections", true)
|
|
||||||
config.EnableIPv6 = readBool(reader, "Is your server IPv6 capable?", true)
|
|
||||||
|
|
||||||
if !config.HybridMode {
|
|
||||||
// Email configuration
|
// Email configuration
|
||||||
fmt.Println("\n=== Email Configuration ===")
|
fmt.Println("\n=== Email Configuration ===")
|
||||||
config.EnableEmail = readBool(reader, "Enable email functionality (SMTP)", false)
|
config.EnableEmail = readBool(reader, "Enable email functionality (SMTP)", false)
|
||||||
|
|
||||||
if config.EnableEmail {
|
if config.EnableEmail {
|
||||||
config.EmailSMTPHost = readString(reader, "Enter SMTP host", "")
|
config.EmailSMTPHost = readString(reader, "Enter SMTP host", "")
|
||||||
config.EmailSMTPPort = readInt(reader, "Enter SMTP port (default 587)", 587)
|
config.EmailSMTPPort = readInt(reader, "Enter SMTP port (default 587)", 587)
|
||||||
|
@ -348,23 +362,29 @@ func collectUserInput(reader *bufio.Reader) Config {
|
||||||
config.EmailSMTPPass = readString(reader, "Enter SMTP password", "") // Should this be readPassword?
|
config.EmailSMTPPass = readString(reader, "Enter SMTP password", "") // Should this be readPassword?
|
||||||
config.EmailNoReply = readString(reader, "Enter no-reply email address", "")
|
config.EmailNoReply = readString(reader, "Enter no-reply email address", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if config.BaseDomain == "" {
|
if config.BaseDomain == "" {
|
||||||
fmt.Println("Error: Domain name is required")
|
fmt.Println("Error: Domain name is required")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if config.DashboardDomain == "" {
|
|
||||||
fmt.Println("Error: Dashboard Domain name is required")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if config.LetsEncryptEmail == "" {
|
if config.LetsEncryptEmail == "" {
|
||||||
fmt.Println("Error: Let's Encrypt email is required")
|
fmt.Println("Error: Let's Encrypt email is required")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advanced configuration
|
||||||
|
|
||||||
|
fmt.Println("\n=== Advanced Configuration ===")
|
||||||
|
|
||||||
|
config.EnableIPv6 = readBool(reader, "Is your server IPv6 capable?", true)
|
||||||
|
|
||||||
|
if config.DashboardDomain == "" {
|
||||||
|
fmt.Println("Error: Dashboard Domain name is required")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,6 +413,11 @@ func createConfigFiles(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the hybrid does not need the dynamic config
|
||||||
|
if config.HybridMode && strings.Contains(path, "dynamic_config.yml") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// skip .DS_Store
|
// skip .DS_Store
|
||||||
if strings.Contains(path, ".DS_Store") {
|
if strings.Contains(path, ".DS_Store") {
|
||||||
return nil
|
return nil
|
||||||
|
@ -443,6 +468,7 @@ func createConfigFiles(config Config) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyFile(src, dst string) error {
|
func copyFile(src, dst string) error {
|
||||||
source, err := os.Open(src)
|
source, err := os.Open(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue