Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for the time zone configurations #698

Merged
merged 4 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading