Skip to content

Commit

Permalink
Add documentation for the time zone configurations (#698)
Browse files Browse the repository at this point in the history
  • Loading branch information
yohamta authored Nov 6, 2024
1 parent 93c3710 commit 4bbcc71
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 126 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ run-server-https: ${SERVER_CERT_FILE} ${SERVER_KEY_FILE}
go run . start-all

# test runs all tests.
test:
test: build-bin
@echo "${COLOR_GREEN}Running tests...${COLOR_RESET}"
@GOBIN=${LOCAL_BIN_DIR} go install ${PKG_gotestsum}
@go clean -testcache
Expand Down
4 changes: 4 additions & 0 deletions docs/source/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The following environment variables can be used to configure the Dagu. Default v
- ``DAGU_WORK_DIR``: The working directory for DAGs. If not set, the default value is DAG location. Also you can set the working directory for each DAG steps in the DAG configuration file. For more information, see :ref:`specifying working dir`.
- ``DAGU_CERT_FILE``: The path to the SSL certificate file.
- ``DAGU_KEY_FILE`` : The path to the SSL key file.
- ``DAGU_TZ`` (``""``): The timezone to use for the server. By default, the server will use the system's local timezone.

Config File
--------------
Expand Down Expand Up @@ -69,6 +70,9 @@ You can create ``admin.yaml`` file in ``$HOME/.config/dagu/`` to override the de
tls:
certFile: <path to SSL certificate file>
keyFile: <path to SSL key file>
# Timezone Configuration
tz: <timezone> # default: "" (e.g. "Asia/Tokyo")
.. _Host and Port Configuration:

Expand Down
72 changes: 46 additions & 26 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,40 @@ import (
"strconv"
"strings"
"sync"
"time"

"github.com/adrg/xdg"
"github.com/spf13/viper"
)

// Config represents the configuration for the server.
type Config struct {
Host string // Server host
Port int // Server port
DAGs string // Location of DAG files
Executable string // Executable path
WorkDir string // Default working directory
IsBasicAuth bool // Enable basic auth
BasicAuthUsername string // Basic auth username
BasicAuthPassword string // Basic auth password
LogEncodingCharset string // Log encoding charset
LogDir string // Log directory
DataDir string // Data directory
SuspendFlagsDir string // Suspend flags directory
AdminLogsDir string // Directory for admin logs
BaseConfig string // Common config file for all DAGs.
NavbarColor string // Navbar color for the web UI
NavbarTitle string // Navbar title for the web UI
Env sync.Map // Store environment variables
TLS *TLS // TLS configuration
IsAuthToken bool // Enable auth token for API
AuthToken string // Auth token for API
LatestStatusToday bool // Show latest status today or the latest status
APIBaseURL string // Base URL for API
Debug bool // Enable debug mode (verbose logging)
LogFormat string // Log format
TimeZone string // The server time zone
Host string // Server host
Port int // Server port
DAGs string // Location of DAG files
Executable string // Executable path
WorkDir string // Default working directory
IsBasicAuth bool // Enable basic auth
BasicAuthUsername string // Basic auth username
BasicAuthPassword string // Basic auth password
LogEncodingCharset string // Log encoding charset
LogDir string // Log directory
DataDir string // Data directory
SuspendFlagsDir string // Suspend flags directory
AdminLogsDir string // Directory for admin logs
BaseConfig string // Common config file for all DAGs.
NavbarColor string // Navbar color for the web UI
NavbarTitle string // Navbar title for the web UI
Env sync.Map // Store environment variables
TLS *TLS // TLS configuration
IsAuthToken bool // Enable auth token for API
AuthToken string // Auth token for API
LatestStatusToday bool // Show latest status today or the latest status
APIBaseURL string // Base URL for API
Debug bool // Enable debug mode (verbose logging)
LogFormat string // Log format
TZ string // The server time zone
Location *time.Location // The server location
}

type TLS struct {
Expand Down Expand Up @@ -98,6 +100,24 @@ func Load() (*Config, error) {
return true
})

if cfg.TZ != "" {
loc, err := time.LoadLocation(cfg.TZ)
if err != nil {
return nil, fmt.Errorf("failed to load timezone: %w", err)
}
cfg.Location = loc
os.Setenv("TZ", cfg.TZ)
} else {
// Load local timezone if not set.
_, offset := time.Now().Zone()
if offset == 0 {
cfg.TZ = "UTC"
} else {
cfg.TZ = fmt.Sprintf("UTC%+d", offset/3600)
}
cfg.Location = time.Local
}

return &cfg, nil
}

Expand Down Expand Up @@ -179,7 +199,7 @@ func bindEnvs() {
_ = viper.BindEnv("navbarColor", "DAGU_NAVBAR_COLOR")
_ = viper.BindEnv("navbarTitle", "DAGU_NAVBAR_TITLE")
_ = viper.BindEnv("apiBaseURL", "DAGU_API_BASE_URL")
_ = viper.BindEnv("timeZone", "DAGU_TIME_ZONE")
_ = viper.BindEnv("tz", "DAGU_TZ")

// Basic authentication
_ = viper.BindEnv("isBasicAuth", "DAGU_IS_BASICAUTH")
Expand Down
2 changes: 1 addition & 1 deletion internal/frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func New(cfg *config.Config, lg logger.Logger, cli client.Client) *server.Server
NavbarColor: cfg.NavbarColor,
NavbarTitle: cfg.NavbarTitle,
APIBaseURL: cfg.APIBaseURL,
TimeZone: cfg.TimeZone,
TimeZone: cfg.TZ,
}

if cfg.IsAuthToken {
Expand Down
2 changes: 1 addition & 1 deletion internal/frontend/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func New(params NewServerArgs) *Server {
NavbarColor: params.NavbarColor,
NavbarTitle: params.NavbarTitle,
APIBaseURL: params.APIBaseURL,
TimeZone: params.TimeZone,
TZ: params.TimeZone,
},
}
}
Expand Down
6 changes: 3 additions & 3 deletions internal/frontend/server/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type funcsConfig struct {
NavbarColor string
NavbarTitle string
APIBaseURL string
TimeZone string
TZ string
}

func defaultFunctions(cfg funcsConfig) template.FuncMap {
Expand All @@ -81,8 +81,8 @@ func defaultFunctions(cfg funcsConfig) template.FuncMap {
"apiURL": func() string {
return cfg.APIBaseURL
},
"timeZone": func() string {
return cfg.TimeZone
"tz": func() string {
return cfg.TZ
},
}
}
Expand Down
42 changes: 21 additions & 21 deletions internal/frontend/templates/base.gohtml
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ navbarTitle }}</title>
<script>
function getConfig() {
return {
apiURL: "{{ apiURL }}",
title: "{{ navbarTitle }}",
navbarColor: "{{ navbarColor }}",
version: "{{ version }}",
timeZone: "{{ timeZone }}",
};
}
</script>
<script defer="defer" src="/assets/bundle.js?v={{ version }}"></script>
</head>
<body>
{{template "content" .}}
</body>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ navbarTitle }}</title>
<script>
function getConfig() {
return {
apiURL: "{{ apiURL }}",
title: "{{ navbarTitle }}",
navbarColor: "{{ navbarColor }}",
version: "{{ version }}",
tz: "{{ tz }}",
};
}
</script>
<script defer="defer" src="/assets/bundle.js?v={{ version }}"></script>
</head>
<body>
{{template "content" .}}
</body>
</html>
{{ end }}
17 changes: 5 additions & 12 deletions internal/scheduler/entryreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,18 @@ type entryReaderImpl struct {
client client.Client
}

type newEntryReaderArgs struct {
DagsDir string
JobCreator jobCreator
Logger logger.Logger
Client client.Client
}

type jobCreator interface {
CreateJob(workflow *dag.DAG, next time.Time) job
}

func newEntryReader(args newEntryReaderArgs) *entryReaderImpl {
func newEntryReader(dagsDir string, jobCreator jobCreator, logger logger.Logger, client client.Client) *entryReaderImpl {
er := &entryReaderImpl{
dagsDir: args.DagsDir,
dagsDir: dagsDir,
dagsLock: sync.Mutex{},
dags: map[string]*dag.DAG{},
jobCreator: args.JobCreator,
logger: args.Logger,
client: args.Client,
jobCreator: jobCreator,
logger: logger,
client: client,
}
if err := er.initDags(); err != nil {
er.logger.Error("DAG initialization failed", "error", err)
Expand Down
24 changes: 12 additions & 12 deletions internal/scheduler/entryreader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,23 @@ func TestReadEntries(t *testing.T) {
}()

now := time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC).Add(-time.Second)
entryReader := newEntryReader(newEntryReaderArgs{
DagsDir: filepath.Join(testdataDir, "invalid_directory"),
JobCreator: &mockJobFactory{},
Logger: test.NewLogger(),
Client: cli,
})
entryReader := newEntryReader(
filepath.Join(testdataDir, "invalid_directory"),
&mockJobFactory{},
test.NewLogger(),
cli,
)

entries, err := entryReader.Read(now)
require.NoError(t, err)
require.Len(t, entries, 0)

entryReader = newEntryReader(newEntryReaderArgs{
DagsDir: testdataDir,
JobCreator: &mockJobFactory{},
Logger: test.NewLogger(),
Client: cli,
})
entryReader = newEntryReader(
testdataDir,
&mockJobFactory{},
test.NewLogger(),
cli,
)

done := make(chan any)
defer close(done)
Expand Down
44 changes: 18 additions & 26 deletions internal/scheduler/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,17 @@ type Scheduler struct {
stop chan struct{}
running atomic.Bool
logger logger.Logger
location *time.Location
}

func New(cfg *config.Config, lg logger.Logger, cli client.Client) *Scheduler {
return newScheduler(newSchedulerArgs{
EntryReader: newEntryReader(newEntryReaderArgs{
Client: cli,
DagsDir: cfg.DAGs,
JobCreator: &jobCreatorImpl{
WorkDir: cfg.WorkDir,
Client: cli,
Executable: cfg.Executable,
},
Logger: lg,
}),
Logger: lg,
LogDir: cfg.LogDir,
})
func New(cfg *config.Config, logger logger.Logger, cli client.Client) *Scheduler {
jobCreator := &jobCreatorImpl{
WorkDir: cfg.WorkDir,
Client: cli,
Executable: cfg.Executable,
}
entryReader := newEntryReader(cfg.DAGs, jobCreator, logger, cli)
return newScheduler(entryReader, logger, cfg.LogDir, cfg.Location)
}

type entryReader interface {
Expand Down Expand Up @@ -123,18 +117,16 @@ func (e *entry) Invoke() error {
}
}

type newSchedulerArgs struct {
EntryReader entryReader
Logger logger.Logger
LogDir string
}

func newScheduler(args newSchedulerArgs) *Scheduler {
func newScheduler(entryReader entryReader, logger logger.Logger, logDir string, location *time.Location) *Scheduler {
if location == nil {
location = time.Local
}
return &Scheduler{
entryReader: args.EntryReader,
logDir: args.LogDir,
entryReader: entryReader,
logDir: logDir,
stop: make(chan struct{}),
logger: args.Logger,
logger: logger,
location: location,
}
}

Expand Down Expand Up @@ -188,7 +180,7 @@ func (s *Scheduler) start() {
}

func (s *Scheduler) run(now time.Time) {
entries, err := s.entryReader.Read(now.Add(-time.Second))
entries, err := s.entryReader.Read(now.Add(-time.Second).In(s.location))
if err != nil {
s.logger.Error("Scheduler failed to read workflow entries", "error", err)
return
Expand Down
Loading

0 comments on commit 4bbcc71

Please sign in to comment.