diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 608bd9e1354..99e5041be97 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -27,6 +27,8 @@ jobs: goos: linux - os: ubuntu-24.04 goos: freebsd + - os: ubuntu-24.04 + goos: darwin # FIXME: this is currently failing in a non-sensical way, so, running on linux instead... # - os: windows-2022 - os: ubuntu-24.04 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4634cc0eb02..4afef00e646 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,6 +74,8 @@ jobs: goos: windows - os: ubuntu-24.04 goos: linux + - os: macos-15 + goos: darwin steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: diff --git a/cmd/nerdctl/completion/completion_freebsd.go b/cmd/nerdctl/completion/completion_unix_nolinux.go similarity index 96% rename from cmd/nerdctl/completion/completion_freebsd.go rename to cmd/nerdctl/completion/completion_unix_nolinux.go index 465671cfc24..5a80c80573c 100644 --- a/cmd/nerdctl/completion/completion_freebsd.go +++ b/cmd/nerdctl/completion/completion_unix_nolinux.go @@ -1,3 +1,5 @@ +//go:build unix && !linux + /* Copyright The containerd Authors. diff --git a/cmd/nerdctl/container/container_cp_freebsd.go b/cmd/nerdctl/container/container_cp_nolinux.go similarity index 97% rename from cmd/nerdctl/container/container_cp_freebsd.go rename to cmd/nerdctl/container/container_cp_nolinux.go index 4e7d2cfd518..95a9accec0a 100644 --- a/cmd/nerdctl/container/container_cp_freebsd.go +++ b/cmd/nerdctl/container/container_cp_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/cmd/nerdctl/container/container_cp_windows.go b/cmd/nerdctl/container/container_cp_windows.go deleted file mode 100644 index 4e7d2cfd518..00000000000 --- a/cmd/nerdctl/container/container_cp_windows.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package container - -import "github.com/spf13/cobra" - -func AddCpCommand(rootCmd *cobra.Command) { - // NOP -} diff --git a/cmd/nerdctl/container/container_run_windows.go b/cmd/nerdctl/container/container_run_nolinux.go similarity index 97% rename from cmd/nerdctl/container/container_run_windows.go rename to cmd/nerdctl/container/container_run_nolinux.go index 5ef9a6d94fb..334461c583e 100644 --- a/cmd/nerdctl/container/container_run_windows.go +++ b/cmd/nerdctl/container/container_run_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/cmd/nerdctl/main_freebsd.go b/cmd/nerdctl/main_nolinux.go similarity index 97% rename from cmd/nerdctl/main_freebsd.go rename to cmd/nerdctl/main_nolinux.go index 391d34cfeed..6460fc0ef8c 100644 --- a/cmd/nerdctl/main_freebsd.go +++ b/cmd/nerdctl/main_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/go.mod b/go.mod index 7738178fa1c..5d1d93e8384 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/docker/docker v28.0.1+incompatible github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 - github.com/fahedouch/go-logrotate v0.2.1 + github.com/fahedouch/go-logrotate v0.2.2-0.20241125150317-0240e7abd7a0 github.com/fatih/color v1.18.0 github.com/fluent/fluent-logger-golang v1.9.0 github.com/fsnotify/fsnotify v1.8.0 diff --git a/go.sum b/go.sum index 0c376fee67b..0830f0efdc4 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -102,8 +102,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fahedouch/go-logrotate v0.2.1 h1:Q0Hk9Kp/Y4iwy9uR9e/60fEoxGhvfk8MG7WwtL9aarM= -github.com/fahedouch/go-logrotate v0.2.1/go.mod h1:Mmyex1f9fGXBNnhS9uHsbnO9BGvADF4VGqVnqAJalgc= +github.com/fahedouch/go-logrotate v0.2.2-0.20241125150317-0240e7abd7a0 h1:8G3ndPQqhkZPriZQrTX1AXM5uA6/Bqa1NelOCOwrftU= +github.com/fahedouch/go-logrotate v0.2.2-0.20241125150317-0240e7abd7a0/go.mod h1:v58eXg33TCKxsBV7Qzgi6/IqgGaEC0i8cwZsrnHKtwM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= diff --git a/pkg/buildkitutil/buildkitutil_test.go b/pkg/buildkitutil/buildkitutil_test.go index 09250c0439a..a123bc5f3cd 100644 --- a/pkg/buildkitutil/buildkitutil_test.go +++ b/pkg/buildkitutil/buildkitutil_test.go @@ -35,6 +35,8 @@ func TestBuildKitFile(t *testing.T) { var tmp = t.TempDir() var wd, err = os.Getwd() assert.NilError(t, err) + tmp, err = filepath.EvalSymlinks(tmp) + assert.NilError(t, err) err = os.Chdir(tmp) assert.NilError(t, err) defer os.Chdir(wd) diff --git a/pkg/buildkitutil/buildkitutil_freebsd.go b/pkg/buildkitutil/buildkitutil_unix_nolinux.go similarity index 96% rename from pkg/buildkitutil/buildkitutil_freebsd.go rename to pkg/buildkitutil/buildkitutil_unix_nolinux.go index a0b02e3c7dc..238f8677198 100644 --- a/pkg/buildkitutil/buildkitutil_freebsd.go +++ b/pkg/buildkitutil/buildkitutil_unix_nolinux.go @@ -1,3 +1,5 @@ +//go:build unix && !linux + /* Copyright The containerd Authors. diff --git a/pkg/cmd/container/exec_freebsd.go b/pkg/cmd/container/exec_nolinux.go similarity index 97% rename from pkg/cmd/container/exec_freebsd.go rename to pkg/cmd/container/exec_nolinux.go index 57ebca59e4a..99865ab4ea8 100644 --- a/pkg/cmd/container/exec_freebsd.go +++ b/pkg/cmd/container/exec_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/pkg/cmd/container/exec_windows.go b/pkg/cmd/container/exec_windows.go deleted file mode 100644 index 0e8eb6bc563..00000000000 --- a/pkg/cmd/container/exec_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package container - -import ( - "github.com/opencontainers/runtime-spec/specs-go" -) - -func setExecCapabilities(pspec *specs.Process) error { - //no op windows - return nil -} diff --git a/pkg/cmd/container/run_freebsd.go b/pkg/cmd/container/run_unix_nolinux.go similarity index 97% rename from pkg/cmd/container/run_freebsd.go rename to pkg/cmd/container/run_unix_nolinux.go index ddf743121e7..fde2629b27d 100644 --- a/pkg/cmd/container/run_freebsd.go +++ b/pkg/cmd/container/run_unix_nolinux.go @@ -1,3 +1,5 @@ +//go:build unix && !linux + /* Copyright The containerd Authors. diff --git a/pkg/cmd/container/stats_freebsd.go b/pkg/cmd/container/stats_nolinux.go similarity index 98% rename from pkg/cmd/container/stats_freebsd.go rename to pkg/cmd/container/stats_nolinux.go index ef2c98fdfad..fbef460eaab 100644 --- a/pkg/cmd/container/stats_freebsd.go +++ b/pkg/cmd/container/stats_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/pkg/cmd/container/stats_windows.go b/pkg/cmd/container/stats_windows.go deleted file mode 100644 index ef2c98fdfad..00000000000 --- a/pkg/cmd/container/stats_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package container - -import ( - "github.com/containerd/nerdctl/v2/pkg/inspecttypes/native" - "github.com/containerd/nerdctl/v2/pkg/statsutil" -) - -func setContainerStatsAndRenderStatsEntry(previousStats *statsutil.ContainerStats, firstSet bool, anydata interface{}, pid int, interfaces []native.NetInterface) (statsutil.StatsEntry, error) { - return statsutil.StatsEntry{}, nil -} diff --git a/pkg/cmd/namespace/namespace_freebsd.go b/pkg/cmd/namespace/namespace_nolinux.go similarity index 97% rename from pkg/cmd/namespace/namespace_freebsd.go rename to pkg/cmd/namespace/namespace_nolinux.go index a3a45d59168..eb133db4beb 100644 --- a/pkg/cmd/namespace/namespace_freebsd.go +++ b/pkg/cmd/namespace/namespace_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/pkg/cmd/namespace/namespace_windows.go b/pkg/cmd/namespace/namespace_windows.go deleted file mode 100644 index a3a45d59168..00000000000 --- a/pkg/cmd/namespace/namespace_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package namespace - -import ( - "github.com/containerd/containerd/v2/pkg/namespaces" -) - -func namespaceDeleteOpts(cgroup bool) ([]namespaces.DeleteOpts, error) { - var delOpts []namespaces.DeleteOpts - return delOpts, nil -} diff --git a/pkg/containerinspector/containerinspector_freebsd.go b/pkg/containerinspector/containerinspector_unix_nolinux.go similarity index 96% rename from pkg/containerinspector/containerinspector_freebsd.go rename to pkg/containerinspector/containerinspector_unix_nolinux.go index e5a2dbc42fb..2902651bc61 100644 --- a/pkg/containerinspector/containerinspector_freebsd.go +++ b/pkg/containerinspector/containerinspector_unix_nolinux.go @@ -1,3 +1,5 @@ +//go:build unix && !linux + /* Copyright The containerd Authors. diff --git a/pkg/defaults/defaults_darwin.go b/pkg/defaults/defaults_darwin.go index 38db7823db1..88aef1073e4 100644 --- a/pkg/defaults/defaults_darwin.go +++ b/pkg/defaults/defaults_darwin.go @@ -20,22 +20,42 @@ package defaults +import gocni "github.com/containerd/go-cni" + +const ( + AppArmorProfileName = "" + SeccompProfileName = "" + Runtime = "" +) + func CNIPath() string { - return "" + return gocni.DefaultCNIDir +} + +func CNIRuntimeDir() (string, error) { + return "/var/run/cni", nil } func CNINetConfPath() string { - return "" + return gocni.DefaultNetDir } func DataRoot() string { - return "" + return "/var/lib/nerdctl" } func CgroupManager() string { return "" } +func CgroupnsMode() string { + return "" +} + +func NerdctlTOML() string { + return "/etc/nerdctl/nerdctl.toml" +} + func HostsDirs() []string { return []string{} } diff --git a/pkg/defaults/defaults_freebsd.go b/pkg/defaults/defaults_freebsd.go index 90cdda10a9a..cf768925f1c 100644 --- a/pkg/defaults/defaults_freebsd.go +++ b/pkg/defaults/defaults_freebsd.go @@ -40,7 +40,7 @@ func CNINetConfPath() string { } func CNIRuntimeDir() (string, error) { - return "/run/cni", nil + return "/var/run/cni", nil } func CgroupManager() string { diff --git a/cmd/nerdctl/main_windows.go b/pkg/infoutil/infoutil_darwin.go similarity index 62% rename from cmd/nerdctl/main_windows.go rename to pkg/infoutil/infoutil_darwin.go index 391d34cfeed..3b87f89df2f 100644 --- a/cmd/nerdctl/main_windows.go +++ b/pkg/infoutil/infoutil_darwin.go @@ -14,16 +14,25 @@ limitations under the License. */ -package main +package infoutil import ( - "github.com/spf13/cobra" + "github.com/docker/docker/pkg/sysinfo" + + "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" ) -func appNeedsRootlessParentMain(cmd *cobra.Command, args []string) bool { - return false +const UnameO = "Darwin" + +func CgroupsVersion() string { + return "" +} + +func fulfillPlatformInfo(info *dockercompat.Info) { + // unimplemented } -func addApparmorCommand(rootCmd *cobra.Command) { - // NOP +func mobySysInfo(info *dockercompat.Info) *sysinfo.SysInfo { + var sysinfo sysinfo.SysInfo + return &sysinfo } diff --git a/pkg/mountutil/mountutil_darwin.go b/pkg/mountutil/mountutil_darwin.go new file mode 100644 index 00000000000..c86d9a3cdec --- /dev/null +++ b/pkg/mountutil/mountutil_darwin.go @@ -0,0 +1,72 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package mountutil + +import ( + "fmt" + "strings" + + "github.com/containerd/containerd/v2/pkg/oci" + "github.com/containerd/errdefs" + "github.com/containerd/log" + + "github.com/containerd/nerdctl/v2/pkg/mountutil/volumestore" +) + +const ( + DefaultMountType = "" + + DefaultPropagationMode = "" +) + +func UnprivilegedMountFlags(path string) ([]string, error) { + m := []string{} + return m, nil +} + +// parseVolumeOptions parses specified optsRaw with using information of +// the volume type and the src directory when necessary. +func parseVolumeOptions(vType, src, optsRaw string) ([]string, []oci.SpecOpts, error) { + var writeModeRawOpts []string + for _, opt := range strings.Split(optsRaw, ",") { + switch opt { + case "rw": + writeModeRawOpts = append(writeModeRawOpts, opt) + case "ro": + writeModeRawOpts = append(writeModeRawOpts, opt) + case "": + // NOP + default: + log.L.Warnf("unsupported volume option %q", opt) + } + } + var opts []string + if len(writeModeRawOpts) > 1 { + return nil, nil, fmt.Errorf("duplicated read/write volume option: %+v", writeModeRawOpts) + } else if len(writeModeRawOpts) > 0 && writeModeRawOpts[0] == "ro" { + opts = append(opts, "ro") + } // No need to return option when "rw" + return opts, nil, nil +} + +func ProcessFlagTmpfs(s string) (*Processed, error) { + return nil, errdefs.ErrNotImplemented +} + +func ProcessFlagMount(s string, volStore volumestore.VolumeStore) (*Processed, error) { + return nil, errdefs.ErrNotImplemented +} diff --git a/pkg/ocihook/ocihook_freebsd.go b/pkg/ocihook/ocihook_freebsd.go deleted file mode 100644 index 03323a67215..00000000000 --- a/pkg/ocihook/ocihook_freebsd.go +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright The containerd Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package ocihook - -func loadAppArmor() { - //noop -} diff --git a/pkg/ocihook/ocihook_windows.go b/pkg/ocihook/ocihook_nolinux.go similarity index 97% rename from pkg/ocihook/ocihook_windows.go rename to pkg/ocihook/ocihook_nolinux.go index 03323a67215..85f465f392f 100644 --- a/pkg/ocihook/ocihook_windows.go +++ b/pkg/ocihook/ocihook_nolinux.go @@ -1,3 +1,5 @@ +//go:build !linux + /* Copyright The containerd Authors. diff --git a/cmd/nerdctl/container/container_run_freebsd.go b/pkg/testutil/nerdtest/platform/platform_darwin.go similarity index 59% rename from cmd/nerdctl/container/container_run_freebsd.go rename to pkg/testutil/nerdtest/platform/platform_darwin.go index 5ef9a6d94fb..0fa050fe63f 100644 --- a/cmd/nerdctl/container/container_run_freebsd.go +++ b/pkg/testutil/nerdtest/platform/platform_darwin.go @@ -14,13 +14,16 @@ limitations under the License. */ -package container +package platform -import ( - "github.com/spf13/cobra" -) - -func capShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - candidates := []string{} - return candidates, cobra.ShellCompDirectiveNoFileComp +func DataHome() (string, error) { + panic("not supported") } + +var ( + // The following are here solely for darwin to compile / lint. They are not used, as the corresponding tests are running only on linux. + RegistryImageStable = "registry:2" + RegistryImageNext = "ghcr.io/distribution/distribution:" + KuboImage = "ipfs/kubo:v0.16.0" + DockerAuthImage = "cesanta/docker_auth:1.7" +) diff --git a/pkg/testutil/testutil_darwin.go b/pkg/testutil/testutil_darwin.go new file mode 100644 index 00000000000..94907ae745c --- /dev/null +++ b/pkg/testutil/testutil_darwin.go @@ -0,0 +1,42 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package testutil + +import "fmt" + +const ( + CommonImage = "" + + // This error string is expected when attempting to connect to a TCP socket + // for a service which actively refuses the connection. + // (e.g. attempting to connect using http to an https endpoint). + // It should be "connection refused" as per the TCP RFC. + // https://www.rfc-editor.org/rfc/rfc793 + ExpectedConnectionRefusedError = "connection refused" +) + +var ( + BusyboxImage = "ghcr.io/containerd/busybox:1.36" + AlpineImage = mirrorOf("alpine:3.13") + NginxAlpineImage = mirrorOf("nginx:1.19-alpine") + GolangImage = mirrorOf("golang:1.18") +) + +func mirrorOf(s string) string { + // plain mirror, NOT stargz-converted images + return fmt.Sprintf("ghcr.io/stargz-containers/%s-org", s) +}