Skip to content

Commit

Permalink
Merge pull request cri-o#7589 from sohankunkerkar/add-timezone-support
Browse files Browse the repository at this point in the history
*: add support for specifying timezone for pod/container
  • Loading branch information
openshift-merge-bot[bot] committed Feb 9, 2024
2 parents 59ea87d + a38b9b9 commit b4bd55d
Show file tree
Hide file tree
Showing 24 changed files with 327 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ linters-settings:
# - filepathJoin
# - whyNoLint
gocyclo:
min-complexity: 160
min-complexity: 165
nakedret:
max-func-lines: 15
goconst:
Expand Down
1 change: 1 addition & 0 deletions completions/bash/crio
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ h
--stream-tls-ca
--stream-tls-cert
--stream-tls-key
--timezone
--tracing-endpoint
--tracing-sampling-rate-per-million
--uid-mappings
Expand Down
1 change: 1 addition & 0 deletions completions/fish/crio.fish
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ complete -c crio -n '__fish_crio_no_subcommand' -f -l stream-port -r -d 'Bind po
complete -c crio -n '__fish_crio_no_subcommand' -l stream-tls-ca -r -d 'Path to the x509 CA(s) file used to verify and authenticate client communication with the encrypted stream. This file can change and CRI-O will automatically pick up the changes within 5 minutes.'
complete -c crio -n '__fish_crio_no_subcommand' -l stream-tls-cert -r -d 'Path to the x509 certificate file used to serve the encrypted stream. This file can change and CRI-O will automatically pick up the changes within 5 minutes.'
complete -c crio -n '__fish_crio_no_subcommand' -l stream-tls-key -r -d 'Path to the key file used to serve the encrypted stream. This file can change and CRI-O will automatically pick up the changes within 5 minutes.'
complete -c crio -n '__fish_crio_no_subcommand' -f -l timezone -s tz -r -d 'To set the timezone for a container in CRI-O. If an empty string is provided, CRI-O retains its default behavior. Use \'Local\' to match the timezone of the host machine.'
complete -c crio -n '__fish_crio_no_subcommand' -f -l tracing-endpoint -r -d 'Address on which the gRPC tracing collector will listen.'
complete -c crio -n '__fish_crio_no_subcommand' -f -l tracing-sampling-rate-per-million -r -d 'Number of samples to collect per million OpenTelemetry spans. Set to 1000000 to always sample.'
complete -c crio -n '__fish_crio_no_subcommand' -f -l uid-mappings -r -d 'Specify the UID mappings to use for the user namespace. This option is deprecated, and will be replaced with Kubernetes user namespace support (KEP-127) in the future.'
Expand Down
1 change: 1 addition & 0 deletions completions/zsh/_crio
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ it later with **--config**. Global options will modify the output.'
'--stream-tls-ca'
'--stream-tls-cert'
'--stream-tls-key'
'--timezone'
'--tracing-endpoint'
'--tracing-sampling-rate-per-million'
'--uid-mappings'
Expand Down
3 changes: 3 additions & 0 deletions docs/crio.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ crio
[--stream-tls-ca]=[value]
[--stream-tls-cert]=[value]
[--stream-tls-key]=[value]
[--timezone|--tz]=[value]
[--tracing-endpoint]=[value]
[--tracing-sampling-rate-per-million]=[value]
[--uid-mappings]=[value]
Expand Down Expand Up @@ -420,6 +421,8 @@ crio [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]

**--stream-tls-key**="": Path to the key file used to serve the encrypted stream. This file can change and CRI-O will automatically pick up the changes within 5 minutes.

**--timezone, --tz**="": To set the timezone for a container in CRI-O. If an empty string is provided, CRI-O retains its default behavior. Use 'Local' to match the timezone of the host machine.

**--tracing-endpoint**="": Address on which the gRPC tracing collector will listen. (default: "0.0.0.0:4317")

**--tracing-sampling-rate-per-million**="": Number of samples to collect per million OpenTelemetry spans. Set to 1000000 to always sample. (default: 0)
Expand Down
3 changes: 3 additions & 0 deletions docs/crio.conf.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ Enable CRI-O to generate the container pod-level events in order to optimize the
**disable_hostport_mapping**=false
Enable/Disable the container hostport mapping in CRI-O. Default value is set to 'false'.

**timezone**=""
To set the timezone for a container in CRI-O. If an empty string is provided, CRI-O retains its default behavior. Use 'Local' to match the timezone of the host machine.

### CRIO.RUNTIME.RUNTIMES TABLE
The "crio.runtime.runtimes" table defines a list of OCI compatible runtimes. The runtime to use is picked based on the runtime handler provided by the CRI. If no runtime handler is provided, the runtime will be picked based on the level of trust of the workload. This option supports live configuration reload. This option supports live configuration reload.

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ require (
github.com/containerd/typeurl v1.0.3-0.20220422153119-7f6e6d160d67
github.com/containernetworking/cni v1.1.2
github.com/containernetworking/plugins v1.4.0
github.com/containers/common v0.57.1
github.com/containers/common v0.57.4
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/conmon-rs v0.6.2-0.20230920142715-f5a362044a57
github.com/containers/image/v5 v5.29.0
github.com/containers/image/v5 v5.29.2
github.com/containers/kubensmnt v1.2.0
github.com/containers/ocicrypt v1.1.9
github.com/containers/podman/v4 v4.8.3
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -883,14 +883,14 @@ github.com/containernetworking/plugins v1.4.0 h1:+w22VPYgk7nQHw7KT92lsRmuToHvb7w
github.com/containernetworking/plugins v1.4.0/go.mod h1:UYhcOyjefnrQvKvmmyEKsUA+M9Nfn7tqULPpH0Pkcj0=
github.com/containers/buildah v1.33.2 h1:8xdFFP2txJ5lQP91MqYrs/cGhF4o8f46wzFgnrTDJBM=
github.com/containers/buildah v1.33.2/go.mod h1:P1fI6kIw3d3ENk5eUJjkXeT9YFoG3uAAWglCk5BeuWc=
github.com/containers/common v0.57.1 h1:KWAs4PMPgBFmBV4QNbXhUB8TqvlgR95BJN2sbbXkWHY=
github.com/containers/common v0.57.1/go.mod h1:t/Z+/sFrapvFMEJe3YnecN49/Tae2wYEQShbEN6SRaU=
github.com/containers/common v0.57.4 h1:kmfBad92kUjP5X44BPpOwMe+eZQqaKETfS+ASeL0g+g=
github.com/containers/common v0.57.4/go.mod h1:o3L3CyOI9yr+JC8l4dZgvqTxcjs3qdKmkek00uchgvw=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/conmon-rs v0.6.2-0.20230920142715-f5a362044a57 h1:EHiHMe0cOPvBzi20g/f+NzhjhPFTxpyAkpRE4h1ZAzw=
github.com/containers/conmon-rs v0.6.2-0.20230920142715-f5a362044a57/go.mod h1:rMhWT6H3demwJCFuT+Bc9b+T9oypfZ5SnPzOrt6QNkg=
github.com/containers/image/v5 v5.29.0 h1:9+nhS/ZM7c4Kuzu5tJ0NMpxrgoryOJ2HAYTgG8Ny7j4=
github.com/containers/image/v5 v5.29.0/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E=
github.com/containers/image/v5 v5.29.2 h1:b8U0XYWhaQbKucK73IbmSm8WQyKAhKDbAHQc45XlsOw=
github.com/containers/image/v5 v5.29.2/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E=
github.com/containers/kubensmnt v1.2.0 h1:BDtkaOFQ5fN7FnB9kC6peMW50KkwI1KI8E9ROBFeQIg=
github.com/containers/kubensmnt v1.2.0/go.mod h1:1/HG09N/a1+WSD3zkurzeWtqlKRSfUUnlIF/08zloqk=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
Expand Down
10 changes: 10 additions & 0 deletions internal/criocli/criocli.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ func mergeConfig(config *libconfig.Config, ctx *cli.Context) error {
if ctx.IsSet("disable-hostport-mapping") {
config.DisableHostPortMapping = ctx.Bool("disable-hostport-mapping")
}
if ctx.IsSet("timezone") {
config.Timezone = ctx.String("timezone")
}
return nil
}

Expand Down Expand Up @@ -1182,6 +1185,13 @@ func getCrioFlags(defConf *libconfig.Config) []cli.Flag {
EnvVars: []string{"DISABLE_HOSTPORT_MAPPING"},
Value: defConf.DisableHostPortMapping,
},
&cli.StringFlag{
Name: "timezone",
Aliases: []string{"tz"},
Usage: "To set the timezone for a container in CRI-O. If an empty string is provided, CRI-O retains its default behavior. Use 'Local' to match the timezone of the host machine.",
EnvVars: []string{"CONTAINER_TIME_ZONE"},
Value: defConf.Timezone,
},
}
}

Expand Down
5 changes: 5 additions & 0 deletions internal/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ func (r *Runtime) RuntimeType(runtimeHandler string) (string, error) {
return rh.RuntimeType, nil
}

// Timezone returns the timezone configured inside the container.
func (r *Runtime) Timezone() string {
return r.config.Timezone
}

// RuntimeSupportsIDMap returns whether the runtime of runtimeHandler supports the "runtime features"
// command, and that the output of that command advertises IDMapped mounts as an option
func (r *Runtime) RuntimeSupportsIDMap(runtimeHandler string) bool {
Expand Down
12 changes: 12 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"path/filepath"
"regexp"
"strings"
"time"

"github.com/BurntSushi/toml"
"github.com/containers/common/pkg/hooks"
Expand Down Expand Up @@ -479,6 +480,10 @@ type RuntimeConfig struct {
// Option to disable hostport mapping in CRI-O
// Default value is 'false'
DisableHostPortMapping bool `toml:"disable_hostport_mapping"`

// Option to set the timezone inside the container.
// Use 'Local' to match the timezone of the host machine.
Timezone string `toml:"timezone"`
}

// ImageConfig represents the "crio.image" TOML config table.
Expand Down Expand Up @@ -1056,6 +1061,13 @@ func (c *RuntimeConfig) Validate(systemContext *types.SystemContext, onExecution
return err
}

if c.Timezone != "" && !strings.EqualFold(c.Timezone, "local") {
_, err := time.LoadLocation(c.Timezone)
if err != nil {
return fmt.Errorf("invalid timezone: %s", c.Timezone)
}
}

if c.LogSizeMax >= 0 && c.LogSizeMax < OCIBufSize {
return fmt.Errorf("log size max should be negative or >= %d", OCIBufSize)
}
Expand Down
33 changes: 33 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,39 @@ var _ = t.Describe("Config", func() {
// Then
Expect(err).NotTo(BeNil())
})
It("should pass for valid Timezone", func() {
// Set a valid Timezone
sut.Timezone = "America/New_York"

// When
err := sut.RuntimeConfig.Validate(nil, false)

// Then
Expect(err).To(BeNil())
})

It("should fail for invalid Timezone", func() {
// Set an invalid Timezone
sut.Timezone = "InvalidTimezone"

// When
err := sut.RuntimeConfig.Validate(nil, false)

// Then
Expect(err).NotTo(BeNil())
Expect(err.Error()).To(ContainSubstring("invalid timezone: InvalidTimezone"))
})

It("should pass for 'Local' Timezone", func() {
// Set Timezone to 'Local'
sut.Timezone = "Local"

// When
err := sut.RuntimeConfig.Validate(nil, false)

// Then
Expect(err).To(BeNil())
})
})
t.Describe("TranslateMonitorFields", func() {
It("should fail on invalid conmon cgroup", func() {
Expand Down
11 changes: 11 additions & 0 deletions pkg/config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@ func initCrioTemplateConfig(c *Config) ([]*templateConfigValue, error) {
group: crioRuntimeConfig,
isDefaultValue: simpleEqual(dc.DisableHostPortMapping, c.DisableHostPortMapping),
},
{
templateString: templateStringCrioRuntimeTimezone,
group: crioRuntimeConfig,
isDefaultValue: simpleEqual(dc.Timezone, c.Timezone),
},
{
templateString: templateStringCrioImageDefaultTransport,
group: crioImageConfig,
Expand Down Expand Up @@ -1368,6 +1373,12 @@ const templateStringCrioRuntimeDisableHostPortMapping = `# disable_hostport_mapp
`

const templateStringCrioRuntimeTimezone = `# timezone To set the timezone for a container in CRI-O.
# If an empty string is provided, CRI-O retains its default behavior. Use 'Local' to match the timezone of the host machine.
{{ $.Comment }}timezone = "{{ .Timezone }}"
`

const templateStringCrioImage = `# The crio.image table contains settings pertaining to the management of OCI images.
#
# CRI-O reads its configured registries defaults from the system wide
Expand Down
25 changes: 25 additions & 0 deletions server/container_create_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/containers/common/pkg/subscriptions"
"github.com/containers/common/pkg/timezone"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/pkg/rootless"
selinux "github.com/containers/podman/v4/pkg/selinux"
Expand Down Expand Up @@ -867,6 +868,11 @@ func (s *Server) createSandboxContainer(ctx context.Context, ctr ctrfactory.Cont
return nil, err
}

// Configure timezone for the container if it is set.
if err := configureTimezone(s.Runtime().Timezone(), ociContainer.BundlePath(), mountPoint, mountLabel, etc, ociContainer.ID(), options, ctr); err != nil {
return nil, fmt.Errorf("failed to configure timezone for container %s: %w", ociContainer.ID(), err)
}

if os.Getenv(rootlessEnvName) != "" {
makeOCIConfigurationRootless(specgen)
}
Expand Down Expand Up @@ -921,6 +927,25 @@ func (s *Server) createSandboxContainer(ctx context.Context, ctr ctrfactory.Cont
return ociContainer, nil
}

func configureTimezone(tz, containerRunDir, mountPoint, mountLabel, etcPath, containerID string, options []string, ctr ctrfactory.Container) error {
localTimePath, err := timezone.ConfigureContainerTimeZone(tz, containerRunDir, mountPoint, etcPath, containerID)
if err != nil {
return fmt.Errorf("setting timezone for container %s: %w", containerID, err)
}
if localTimePath != "" {
if err := securityLabel(localTimePath, mountLabel, false, false); err != nil {
return err
}
ctr.SpecAddMount(rspec.Mount{
Destination: "/etc/localtime",
Type: "bind",
Source: localTimePath,
Options: append(options, []string{"bind", "nodev", "nosuid", "noexec"}...),
})
}
return nil
}

func setupWorkingDirectory(rootfs, mountLabel, containerCwd string) error {
fp, err := securejoin.SecureJoin(rootfs, containerCwd)
if err != nil {
Expand Down
26 changes: 26 additions & 0 deletions test/image.bats
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,29 @@ EOF
output=$(crictl images -o json | jq '.images[] | select(.repoTags[] == "quay.io/crio/hello-wasm:latest") | .pinned')
[ "$output" == "true" ]
}

@test "run container in pod with timezone configured" {
CONTAINER_TIME_ZONE="Asia/Singapore" start_crio
jq '.metadata.name = "podsandbox-timezone"
|.image.image = "quay.io/crio/fedora-crio-ci:latest"
| del(.command, .args, .linux.resources)' \
"$TESTDATA"/container_config.json > "$TESTDIR/timezone.json"

ctr_id=$(crictl run "$TESTDIR/timezone.json" "$TESTDATA/sandbox_config.json")
output=$(crictl exec "$ctr_id" date +"%a %b %e %H:%M:%S %Z %Y")
expected_output=$(TZ="Asia/Singapore" date +"%a %b %e %H:%M:%S %Z %Y")
[[ "$output" == *"$expected_output"* ]]
}

@test "run container in pod with local timezone" {
CONTAINER_TIME_ZONE="local" start_crio
jq '.metadata.name = "podsandbox-empty-timezone"
| .image.image = "quay.io/crio/fedora-crio-ci:latest"
| del(.command, .args, .linux.resources)' \
"$TESTDATA"/container_config.json > "$TESTDIR/empty_timezone.json"

ctr_id=$(crictl run "$TESTDIR/empty_timezone.json" "$TESTDATA/sandbox_config.json")
output=$(crictl exec "$ctr_id" date +"%a %b %e %H:%M:%S %Z %Y")
expected_output=$(date +"%a %b %e %H:%M:%S %Z %Y")
[[ "$output" == *"$expected_output"* ]]
}
Loading

0 comments on commit b4bd55d

Please sign in to comment.