Skip to content

Commit

Permalink
Merge pull request #1157 from haoming29/cache-sentinel
Browse files Browse the repository at this point in the history
Add support for checking a sentinel file for the cache
  • Loading branch information
turetske authored Apr 24, 2024
2 parents 726da48 + 82310b6 commit d8ddb32
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 7 deletions.
46 changes: 46 additions & 0 deletions cache/cache_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/***************************************************************
*
* Copyright (C) 2024, Pelican Project, Morgridge Institute for Research
*
* 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 cache

import (
"os"
"path"
"path/filepath"

"github.com/pelicanplatform/pelican/param"
"github.com/pkg/errors"
)

// Check for the sentinel file
func CheckCacheSentinelLocation() error {
if param.Cache_SentinelLocation.IsSet() {
sentinelPath := param.Cache_SentinelLocation.GetString()
dataLoc := param.Cache_DataLocation.GetString()
sentinelPath = path.Clean(sentinelPath)
if path.Base(sentinelPath) != sentinelPath {
return errors.Errorf("invalid Cache.SentinelLocation path. File must not contain a directory. Got %s", sentinelPath)
}
fullPath := filepath.Join(dataLoc, sentinelPath)
_, err := os.Stat(fullPath)
if err != nil {
return errors.Wrapf(err, "failed to open Cache.SentinelLocation %s. Directory check failed", fullPath)
}
}
return nil
}
71 changes: 71 additions & 0 deletions cache/cache_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/***************************************************************
*
* Copyright (C) 2024, Pelican Project, Morgridge Institute for Research
*
* 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 cache

import (
"os"
"path/filepath"
"testing"

"github.com/pelicanplatform/pelican/param"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCheckCacheSentinelLocation(t *testing.T) {
t.Run("sentinel-not-set", func(t *testing.T) {
viper.Reset()
err := CheckCacheSentinelLocation()
assert.NoError(t, err)
})

t.Run("sentinel-contains-dir", func(t *testing.T) {
viper.Reset()
viper.Set(param.Cache_SentinelLocation.GetName(), "/test.txt")
err := CheckCacheSentinelLocation()
require.Error(t, err)
assert.Equal(t, "invalid Cache.SentinelLocation path. File must not contain a directory. Got /test.txt", err.Error())
})

t.Run("sentinel-dne", func(t *testing.T) {
tmpDir := t.TempDir()
viper.Reset()
viper.Set(param.Cache_SentinelLocation.GetName(), "test.txt")
viper.Set(param.Cache_DataLocation.GetName(), tmpDir)
err := CheckCacheSentinelLocation()
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to open Cache.SentinelLocation")
})

t.Run("sentinel-exists", func(t *testing.T) {
tmpDir := t.TempDir()
viper.Reset()

viper.Set(param.Cache_SentinelLocation.GetName(), "test.txt")
viper.Set(param.Cache_DataLocation.GetName(), tmpDir)

file, err := os.Create(filepath.Join(tmpDir, "test.txt"))
require.NoError(t, err)
file.Close()

err = CheckCacheSentinelLocation()
require.NoError(t, err)
})
}
16 changes: 15 additions & 1 deletion docs/parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ description: |+
where each of these has the same effect as the corresponding "Origin.Enable*" configuration, except scoped to the
given export. If "PublicReads" is included, "Reads" is inferred.
- SentinelLocation: A filename under `StoragePrefix` path for Pelican to check the storage directory exists and is correctly mounted.
Leave it empty to skip the check. You should always choose a distinguished name for `SentinelLocation`. It should not be reused for other servers.
The value must be a file and contain no directory. Leave it empty to skip the check.
You should always choose a distinct name for `SentinelLocation`. It should not be reused for other servers.
If running in a containerized environment it should not be the name of the underlying physical host as that may change and lead to confusion.
You need to manually create a file under path to `StoragePrefix` with the same name as `SentinelLocation`. Note that this parameter will be ignored
if the origin StorageType is S3.
Expand Down Expand Up @@ -939,6 +941,18 @@ root_default: /run/pelican/xrootd/cache
default: $XDG_RUNTIME_DIR/pelican/cache
components: ["cache"]
---
name: Cache.SentinelLocation
description: |+
A filename under `Cache.DataLocation` path for Pelican to check the storage directory exists and is correctly mounted.
The value must be a file and contain no directory. Leave it empty to skip the check.
You should always choose a distinct name for `Cache.SentinelLocation`. It should not be reused for other servers.
If running in a containerized environment it should not be the name of the underlying physical host as that may change and lead to confusion.
You need to manually create a file under path to `Cache.DataLocation` with the same name as `Cache.SentinelLocation`.
type: filename
default: none
components: ["cache"]
---
name: Cache.XRootDPrefix
description: |+
The directory prefix for the XRootD cache configuration files.
Expand Down
4 changes: 4 additions & 0 deletions launchers/cache_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func CacheServe(ctx context.Context, engine *gin.Engine, egrp *errgroup.Group, m
return nil, err
}

if err := cache.CheckCacheSentinelLocation(); err != nil {
return nil, err
}

cache.RegisterCacheAPI(engine, ctx, egrp)

cacheServer := &cache.CacheServer{}
Expand Down
2 changes: 1 addition & 1 deletion launchers/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func LaunchModules(ctx context.Context, modules config.ServerType) (servers []se
return
}

ok, err = server_utils.CheckSentinelLocation(originExports)
ok, err = server_utils.CheckOriginSentinelLocations(originExports)
if err != nil && !ok {
return
}
Expand Down
1 change: 1 addition & 0 deletions param/parameters.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions param/parameters_struct.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion server_utils/origin.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,8 @@ from S3 service URL. In this configuration, objects can be accessed at /federati
return originExports, nil
}

func CheckSentinelLocation(exports []OriginExport) (ok bool, err error) {
// Check the sentinel files from Origin.Exports
func CheckOriginSentinelLocations(exports []OriginExport) (ok bool, err error) {
for _, export := range exports {
if export.SentinelLocation != "" {
sentinelPath := path.Clean(export.SentinelLocation)
Expand Down
8 changes: 4 additions & 4 deletions server_utils/origin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ func TestGetExports(t *testing.T) {
})
}

func TestCheckSentinelLocation(t *testing.T) {
func TestCheckOriginSentinelLocation(t *testing.T) {
tmpDir := t.TempDir()
tempStn := filepath.Join(tmpDir, "mock_sentinel")
file, err := os.Create(tempStn)
Expand Down Expand Up @@ -437,7 +437,7 @@ func TestCheckSentinelLocation(t *testing.T) {
exports = append(exports, mockExportNoStn)
exports = append(exports, mockExportNoStn)

ok, err := CheckSentinelLocation(exports)
ok, err := CheckOriginSentinelLocations(exports)
assert.NoError(t, err)
assert.True(t, ok)
})
Expand All @@ -447,7 +447,7 @@ func TestCheckSentinelLocation(t *testing.T) {
exports = append(exports, mockExportNoStn)
exports = append(exports, mockExportValidStn)

ok, err := CheckSentinelLocation(exports)
ok, err := CheckOriginSentinelLocations(exports)
assert.NoError(t, err)
assert.True(t, ok)
})
Expand All @@ -458,7 +458,7 @@ func TestCheckSentinelLocation(t *testing.T) {
exports = append(exports, mockExportValidStn)
exports = append(exports, mockExportInvalidStn)

ok, err := CheckSentinelLocation(exports)
ok, err := CheckOriginSentinelLocations(exports)
assert.Error(t, err)
assert.False(t, ok)
})
Expand Down

0 comments on commit d8ddb32

Please sign in to comment.