Skip to content

Commit 50d3996

Browse files
authored
Merge pull request #186 from mauriciopoppe/rmdircontents-filesystem-api
Add RmdirContents API in the Fileystem v2alpha1 API Group
2 parents 184a7ae + b5442d7 commit 50d3996

File tree

17 files changed

+800
-159
lines changed

17 files changed

+800
-159
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ all: build test
44
# include release tools for building binary and testing targets
55
include release-tools/build.make
66

7-
BUILD_PLATFORMS=windows amd64 .exe
7+
BUILD_PLATFORMS=windows amd64 amd64 .exe
88
GOPATH ?= $(shell go env GOPATH)
99
REPO_ROOT = $(CURDIR)
1010
BUILD_DIR = bin

client/api/filesystem/v2alpha1/api.pb.go

Lines changed: 255 additions & 77 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/api/filesystem/v2alpha1/api.proto

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ service Filesystem {
1515
// This may be used for unlinking a symlink created through CreateSymlink.
1616
rpc Rmdir(RmdirRequest) returns (RmdirResponse) {}
1717

18+
// RmdirContents removes the contents of a directory in the host filesystem.
19+
// Unlike Rmdir it won't delete the requested path, it'll only delete its contents.
20+
rpc RmdirContents(RmdirContentsRequest) returns (RmdirContentsResponse) {}
21+
1822
// CreateSymlink creates a symbolic link called target_path that points to source_path
1923
// in the host filesystem (target_path is the name of the symbolic link created,
2024
// source_path is the existing path).
@@ -86,6 +90,29 @@ message RmdirResponse {
8690
// Intentionally empty.
8791
}
8892

93+
message RmdirContentsRequest {
94+
// The path whose contents will be removed in the host's filesystem.
95+
// All special characters allowed by Windows in path names will be allowed
96+
// except for restrictions noted below. For details, please check:
97+
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
98+
//
99+
// Restrictions:
100+
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
101+
// Depending on the context parameter of this function, the path prefix needs
102+
// to match the paths specified either as kubelet-csi-plugins-path
103+
// or as kubelet-pod-path parameters of csi-proxy.
104+
// UNC paths of the form "\\server\share\path\file" are not allowed.
105+
// All directory separators need to be backslash character: "\".
106+
// Characters: .. / : | ? * in the path are not allowed.
107+
// Path cannot be a file of type symlink.
108+
// Maximum path length will be capped to 260 characters.
109+
string path = 1;
110+
}
111+
112+
message RmdirContentsResponse {
113+
// Intentionally empty.
114+
}
115+
89116
message CreateSymlinkRequest {
90117
// The path of the existing directory to be linked.
91118
// All special characters allowed by Windows in path names will be allowed

client/groups/filesystem/v2alpha1/client_generated.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integrationtests/filesystem_v2alpha1_test.go

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package integrationtests
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"io/ioutil"
68
"math/rand"
79
"os"
810
"path/filepath"
@@ -125,7 +127,7 @@ func v2alpha1FilesystemTests(t *testing.T) {
125127
err = os.Mkdir(targetStagePath, os.ModeDir)
126128
require.Nil(t, err)
127129
defer os.Remove(targetStagePath)
128-
// Create a sym link
130+
// Create a symlink
129131
err = os.Symlink(targetStagePath, lnTargetStagePath)
130132
require.Nil(t, err)
131133
defer os.Remove(lnTargetStagePath)
@@ -147,4 +149,99 @@ func v2alpha1FilesystemTests(t *testing.T) {
147149
require.Nil(t, err)
148150
require.Equal(t, isSymlink.IsSymlink, false)
149151
})
152+
t.Run("RmdirContents", func(t *testing.T) {
153+
client, err := v2alpha1client.NewClient()
154+
require.Nil(t, err)
155+
defer client.Close()
156+
157+
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
158+
rand1 := r1.Intn(100)
159+
160+
rootPath := getKubeletPathForTest(fmt.Sprintf("testplugin-%d.csi.io", rand1), t)
161+
// this line should delete the rootPath because only its content were deleted
162+
defer os.RemoveAll(rootPath)
163+
164+
paths := []string{
165+
filepath.Join(rootPath, "foo/goo/"),
166+
filepath.Join(rootPath, "foo/bar/baz/"),
167+
filepath.Join(rootPath, "alpha/beta/gamma/"),
168+
}
169+
for _, path := range paths {
170+
err = os.MkdirAll(path, os.ModeDir)
171+
require.Nil(t, err)
172+
}
173+
174+
rmdirContentsRequest := &v2alpha1.RmdirContentsRequest{
175+
Path: rootPath,
176+
}
177+
_, err = client.RmdirContents(context.Background(), rmdirContentsRequest)
178+
require.Nil(t, err)
179+
180+
// the root path should exist
181+
exists, err := pathExists(rootPath)
182+
assert.True(t, exists, err)
183+
// the root path children shouldn't exist
184+
for _, path := range paths {
185+
exists, err = pathExists(path)
186+
assert.False(t, exists, err)
187+
}
188+
})
189+
190+
t.Run("RmdirContentsNoFollowSymlink", func(t *testing.T) {
191+
// RmdirContents should not delete the target of a symlink, only the symlink
192+
client, err := v2alpha1client.NewClient()
193+
require.Nil(t, err)
194+
defer client.Close()
195+
196+
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
197+
rand1 := r1.Intn(100)
198+
199+
rootPath := getKubeletPathForTest(fmt.Sprintf("testplugin-%d.csi.io", rand1), t)
200+
// this line should delete the rootPath because only its content were deleted
201+
defer os.RemoveAll(rootPath)
202+
203+
insidePath := filepath.Join(rootPath, "inside/")
204+
outsidePath := filepath.Join(rootPath, "outside/")
205+
paths := []string{
206+
filepath.Join(insidePath, "foo/goo/"),
207+
filepath.Join(insidePath, "foo/bar/baz/"),
208+
filepath.Join(insidePath, "foo/beta/gamma/"),
209+
outsidePath,
210+
}
211+
for _, path := range paths {
212+
err = os.MkdirAll(path, os.ModeDir)
213+
require.Nil(t, err)
214+
}
215+
216+
// create a temp file on the outside and make a symlink from the inside to the outside
217+
outsideFile := filepath.Join(outsidePath, "target")
218+
insideFile := filepath.Join(insidePath, "source")
219+
220+
file, err := os.Create(outsideFile)
221+
require.Nil(t, err)
222+
defer file.Close()
223+
err = os.Symlink(outsideFile, insideFile)
224+
require.Nil(t, err)
225+
226+
rmdirContentsRequest := &v2alpha1.RmdirContentsRequest{
227+
Path: insidePath,
228+
}
229+
_, err = client.RmdirContents(context.Background(), rmdirContentsRequest)
230+
require.Nil(t, err)
231+
232+
// the inside path should exist
233+
exists, err := pathExists(insidePath)
234+
require.Nil(t, err)
235+
assert.True(t, exists, "The path shouldn't exist")
236+
// it should have no children
237+
children, err := ioutil.ReadDir(insidePath)
238+
require.Nil(t, err)
239+
assert.True(t, len(children) == 0, "The RmdirContents path to delete shouldn't have children")
240+
// the symlink target should exist
241+
_, err = os.Open(outsideFile)
242+
if errors.Is(err, os.ErrNotExist) {
243+
// the file should exist but it was deleted!
244+
t.Fatalf("File outsideFile=%s doesn't exist", outsideFile)
245+
}
246+
})
150247
}

pkg/os/filesystem/api.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"os/exec"
7+
"path/filepath"
78
"strings"
89
)
910

@@ -18,6 +19,7 @@ type API interface {
1819
PathValid(path string) (bool, error)
1920
Mkdir(path string) error
2021
Rmdir(path string, force bool) error
22+
RmdirContents(path string) error
2123
CreateSymlink(oldname string, newname string) error
2224
IsSymlink(path string) (bool, error)
2325
}
@@ -78,6 +80,29 @@ func (filesystemAPI) Rmdir(path string, force bool) error {
7880
return os.Remove(path)
7981
}
8082

83+
// RmdirContents removes the contents of a directory with `os.RemoveAll`
84+
func (filesystemAPI) RmdirContents(path string) error {
85+
dir, err := os.Open(path)
86+
if err != nil {
87+
return err
88+
}
89+
defer dir.Close()
90+
91+
files, err := dir.Readdirnames(-1)
92+
if err != nil {
93+
return err
94+
}
95+
for _, file := range files {
96+
candidatePath := filepath.Join(path, file)
97+
err = os.RemoveAll(candidatePath)
98+
if err != nil {
99+
return err
100+
}
101+
}
102+
103+
return nil
104+
}
105+
81106
// CreateSymlink creates newname as a symbolic link to oldname.
82107
func (filesystemAPI) CreateSymlink(oldname, newname string) error {
83108
return os.Symlink(oldname, newname)

pkg/server/filesystem/impl/types.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@ type RmdirRequest struct {
6161
type RmdirResponse struct {
6262
}
6363

64+
type RmdirContentsRequest struct {
65+
// The path to remove in the host's filesystem.
66+
// All special characters allowed by Windows in path names will be allowed
67+
// except for restrictions noted below. For details, please check:
68+
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
69+
//
70+
// Restrictions:
71+
// Only absolute path (indicated by a drive letter prefix: e.g. "C:\") is accepted.
72+
// Depending on the context parameter of this function, the path prefix needs
73+
// to match the paths specified either as kubelet-csi-plugins-path
74+
// or as kubelet-pod-path parameters of csi-proxy.
75+
// UNC paths of the form "\\server\share\path\file" are not allowed.
76+
// All directory separators need to be backslash character: "\".
77+
// Characters: .. / : | ? * in the path are not allowed.
78+
// Path cannot be a file of type symlink.
79+
// Maximum path length will be capped to 260 characters.
80+
Path string
81+
}
82+
83+
type RmdirContentsResponse struct {
84+
}
85+
6486
type CreateSymlinkRequest struct {
6587
// The path of the existing directory to be linked.
6688
// All special characters allowed by Windows in path names will be allowed

pkg/server/filesystem/impl/types_generated.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/server/filesystem/impl/v2alpha1/conversion_generated.go

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/server/filesystem/impl/v2alpha1/server_generated.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/server/filesystem/server.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,22 @@ func (s *Server) Rmdir(ctx context.Context, request *internal.RmdirRequest, vers
163163
}
164164
return nil, err
165165
}
166+
167+
func (s *Server) RmdirContents(ctx context.Context, request *internal.RmdirContentsRequest, version apiversion.Version) (*internal.RmdirContentsResponse, error) {
168+
klog.V(2).Infof("Request: RmdirContents with path=%q", request.Path)
169+
err := s.validatePathWindows(request.Path)
170+
if err != nil {
171+
klog.Errorf("failed validatePathWindows %v", err)
172+
return nil, err
173+
}
174+
err = s.hostAPI.RmdirContents(request.Path)
175+
if err != nil {
176+
klog.Errorf("failed RmdirContents %v", err)
177+
return nil, err
178+
}
179+
return nil, err
180+
}
181+
166182
func (s *Server) LinkPath(ctx context.Context, request *internal.LinkPathRequest, version apiversion.Version) (*internal.LinkPathResponse, error) {
167183
klog.V(2).Infof("Request: LinkPath with targetPath=%q sourcePath=%q", request.TargetPath, request.SourcePath)
168184
createSymlinkRequest := &internal.CreateSymlinkRequest{

pkg/server/filesystem/server_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ func (fakeFileSystemAPI) Mkdir(path string) error {
2525
func (fakeFileSystemAPI) Rmdir(path string, force bool) error {
2626
return nil
2727
}
28+
func (fakeFileSystemAPI) RmdirContents(path string) error {
29+
return nil
30+
}
2831
func (fakeFileSystemAPI) CreateSymlink(tgt string, src string) error {
2932
return nil
3033
}

pkg/server/smb/server_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func (fakeFileSystemAPI) Mkdir(path string) error {
4747
func (fakeFileSystemAPI) Rmdir(path string, force bool) error {
4848
return nil
4949
}
50+
func (fakeFileSystemAPI) RmdirContents(path string) error {
51+
return nil
52+
}
5053
func (fakeFileSystemAPI) CreateSymlink(tgt string, src string) error {
5154
return nil
5255
}

scripts/bump-version.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
set -o nounset
1111
set -ex
1212

13-
# The bucket url of this script in Google Cloud, set in sync_scripts
1413
: "${API_GROUP?API_GROUP is not set}"
1514
: "${OLD_API_VERSION:?OLD_API_VERSION is not set, it needs the format vX}"
1615
: "${NEW_API_VERSION:?NEW_API_VERSION is not set, it needs the format vX}"
@@ -40,11 +39,11 @@ function generate_client_files {
4039
rm client/api/$target/api.pb.go || true
4140
rm client/groups/$target/client_generated.go || true
4241

43-
# generate client_generated.go
44-
make generate-csi-proxy-api-gen
4542
# generate api.pb.go
4643
# it's going to fail but it's expected :(
4744
make generate-protobuf || true
45+
# generate client_generated.go
46+
make generate-csi-proxy-api-gen
4847

4948
# restore files from other API groups (side effect of generate-protobuf)
5049
other_leaf_client_files=$(find client/api/ -links 2 -type d -exec echo {} \; | grep -v "$target\$")

0 commit comments

Comments
 (0)