From 87cfa2d79151477f1718894456b9db6d19f415b8 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Fri, 27 Dec 2024 09:43:01 +0000 Subject: [PATCH] feat: support windows host-process deployment --- Makefile | 23 ++ charts/latest/csi-driver-smb-v0.0.0.tgz | Bin 5265 -> 5634 bytes .../csi-smb-node-windows-hostprocess.yaml | 124 +++++++++ charts/latest/csi-driver-smb/values.yaml | 1 + cmd/smbplugin/Dockerfile.WindowsHostProcess | 24 ++ cmd/smbplugin/main.go | 1 + .../safe_mounter_host_process_windows.go | 247 ++++++++++++++++++ pkg/mounter/safe_mounter_unix.go | 2 +- pkg/mounter/safe_mounter_unix_test.go | 2 +- pkg/mounter/safe_mounter_windows.go | 11 +- pkg/os/filesystem/filesystem.go | 166 ++++++++++++ pkg/os/smb/smb.go | 113 ++++++++ pkg/os/smb/smb_test.go | 59 +++++ pkg/smb/fake_mounter.go | 2 +- pkg/smb/smb.go | 5 +- pkg/util/util.go | 19 ++ 16 files changed, 793 insertions(+), 6 deletions(-) create mode 100644 charts/latest/csi-driver-smb/templates/csi-smb-node-windows-hostprocess.yaml create mode 100644 cmd/smbplugin/Dockerfile.WindowsHostProcess create mode 100644 pkg/mounter/safe_mounter_host_process_windows.go create mode 100644 pkg/os/filesystem/filesystem.go create mode 100644 pkg/os/smb/smb.go create mode 100644 pkg/os/smb/smb_test.go diff --git a/Makefile b/Makefile index 36da33ee4c3..6002646be0e 100644 --- a/Makefile +++ b/Makefile @@ -102,11 +102,16 @@ e2e-test: .PHONY: e2e-bootstrap e2e-bootstrap: install-helm +ifdef WINDOWS_USE_HOST_PROCESS_CONTAINERS + (docker pull $(IMAGE_TAG) && docker pull $(IMAGE_TAG)-windows-hp) || make container-all push-manifest +else docker pull $(IMAGE_TAG) || make container-all push-manifest +endif ifdef TEST_WINDOWS helm upgrade csi-driver-smb charts/$(VERSION)/csi-driver-smb --namespace kube-system --wait --timeout=15m -v=5 --debug --install \ ${E2E_HELM_OPTIONS} \ --set windows.enabled=true \ + --set windows.useHostProcessContainers=${WINDOWS_USE_HOST_PROCESS_CONTAINERS} \ --set linux.enabled=false \ --set controller.replicas=1 \ --set controller.logLevel=6 \ @@ -162,6 +167,24 @@ container-windows: -t $(IMAGE_TAG)-windows-$(OSVERSION)-$(ARCH) --build-arg OSVERSION=$(OSVERSION) \ --provenance=false --sbom=false \ --build-arg ARCH=$(ARCH) -f ./cmd/smbplugin/Dockerfile.Windows . +# workaround: only build hostprocess image once +ifdef WINDOWS_USE_HOST_PROCESS_CONTAINERS +ifeq ($(OSVERSION),ltsc2022) + $(MAKE) container-windows-hostprocess + $(MAKE) container-windows-hostprocess-latest +endif +endif + +# Set --provenance=false to not generate the provenance (which is what causes the multi-platform index to be generated, even for a single platform). +.PHONY: container-windows-hostprocess +container-windows-hostprocess: + docker buildx build --pull --output=type=$(OUTPUT_TYPE) --platform="windows/$(ARCH)" --provenance=false --sbom=false \ + -t $(IMAGE_TAG)-windows-hp -f ./cmd/smbplugin/Dockerfile.WindowsHostProcess . + +.PHONY: container-windows-hostprocess-latest +container-windows-hostprocess-latest: + docker buildx build --pull --output=type=$(OUTPUT_TYPE) --platform="windows/$(ARCH)" --provenance=false --sbom=false \ + -t $(IMAGE_TAG_LATEST)-windows-hp -f ./cmd/smbplugin/Dockerfile.WindowsHostProcess . .PHONY: container-all container-all: smb-windows diff --git a/charts/latest/csi-driver-smb-v0.0.0.tgz b/charts/latest/csi-driver-smb-v0.0.0.tgz index e310443510541022dfa203a09c444b6ccdd6a180..87fe3c8272a6b9a915ce0c342ba0d07fb2c27a2c 100644 GIT binary patch delta 5606 zcmVgew812b)SL!&?eayY)yhV-NkToQP9$f z%`cLuOHxj}EdTolKJ=z+`H?j3wSYFZM4m&D=Y0-omlMZhWD3mT!KiyQ!Ax{!IPkyQ z+|}##dIx)Z>Tj>tEC22F4|cxm@4egI-FvsY(|h+_ufNyZ?SFlTdK(-=?G}CQ zag|^0E4gq){s6`a4F_o2Z{a9Pzxr4J7cCFC%Sa^Dz2V0X=xBI~jupwE8_Ljc@d#K5 z0(dLL0Sr))q*~Jye6Q2%^jcr~jkj0+D)Ijm`!Vnh3xN6j-|x%MCH~*pf4AlT=eS6K z$1rFiG{PJ%A%CI+#9&OgV6)EcPrO5Dmy@x>1!Z^)ErfV5lFbof5K&G9WwQYqyG*vc z{fRqV;4uluPOev%2cyW3$0Sr81s)HOi$lz2>U$LX{sr}kI~$jd7;p$h3rSIO z!RVAo0RpUww=2mv%35Ubr~93Lr`G`fC2;bW4fdRAP=9Ed?wCW_@AP*TX{yBkAeSV8f zTZDCEj(_fm@1qfzE}l|iGa7f$5Cjqw-C&;sA~*6Ty2GKm8FP@|8KQS#N8+QQVAS`4 zX@$pwk(1qXI5umMJyr9~ajV5f*ewrPwpPkhYRr~il|lYy$K}LOVykrnSj0>b4}^H+ z!!IEIpne>{P+-9g$X0HQDX8jLTWZ;S;$l8P{eKohUdWSKKk{QPfc;Du7yl$;@+;-y z3+FcPuZ8a5~ z#(&HOu2I_rkQ`$`1W&%n7YP)}@AY~giMh+*KQZupD>sSC2FxPM}R+Glj=PQZ(OXg7jy_%D)F`s7%g4WT(= zL=YGI?>&!({5m>NGE!gG zz$YQP?&lxv>}jQ_l5~DnDOPJ)eYJwKWz4VwTWIU&HTo3#z`3%4u+IQ{Gc>`TWD}$; zHbg=$uF$sb3OjX-BQd*2Q zu1uNbhoiyu6tioejIPbi9~8j^bPB_dA3owJBH{QrmW22z43yoN$bh-pp(Ii%UsaB- zMZEhfE%V+w#JeWSrz2j2qUd^52soiRX~`!9Y8pwB)Xu>u7AU|o=?YBW;CYf%IP2)KNK0B7RkV|00;cWp_SqGB|LN-2^UKq#Kd-Nce-5usKDN^}OO=?J zgm#4(2g%E|T5ik*4SvTX@HOt&uhEgT@m)C_L~jrdLn`#(q*H@EPyO+IBqo3&>hV@Y zz4uuwI~ZS~m;<3W2C2@a+kcEOx?qGdB4$TE<_Z9WJZ8qVlD!d+*_2EnlJ{Ftemr`O`HTw) z99L^)$2B&O#6AQ!maUcd2loPv?hKy2yMl-}g z2&oJ);s)H2h9)tLW(e+a5cz-x@_VVt06j?9@A)S#{hofW*GG>nOCt`hOM~cw(HkNS zs|PWQV1O>;P>=wOP4b8yTc!A)fFM$F-|pG@)yc3U?#22srbY4p-M!sX{BM7McXu2A zdyeaB0xFVFfC#)d{chg0=$0O{*;ll&!T!L!XIZN)M$45B^4a{-4k%EV997QF4EFo2ZpvT8H za-2IX+{rfKFz>46|IZWfBVfEEqIFXN3;4gkf6y=S|H0nQHh=#AEZ4(B_aA6Vf`Qaa zNGc1dDGmcUE$##c=pWt3$5u-!BHo zL^`7r@Po|OMt|0E>E;@k{wxiws&G*@VdzRb%_d^3eF7ZfF?h~sR_Zl0f#|x9#vpun zKnWM-C$Ur8FHNv2Y&2dyBsj2|Wk}+ELT5!O7LME`Mwk`@Xbh7zn+*saaLCw6eoc zx6gsP)rsM740HQQM^UeuoV@%C*gwkQd})R+UA6MR;OZ#nY2~Ew z0{Oq&dsmYGJH5UB-d6rU$5rBvx=7CB7QT|=g@mpjdnH-k$x9a5kVoLEQN)pl1q=nl z0>(3S3n4~*pM>L2>Y2G$4M^vPu3|yO=VsUl8Sg3&Qa(uRRs%ke=BROv6NEU5mYAS~ zJrjW0TsN4NgLJsdBSy%~rlglUA}00yasiMW!hd{NzCIL47-c$I^T^hrSmm`u;8x75@xqqg{oVMkpMU@zFd9w?2B~_u2$v(K`xgH=d zBvhRhygaob}X3ogZ})I5EzyfCapr%|pcivjZwyVQKFgQ>;~4m-MTt zO{C09-{twMZKS8Nc4XRyeP6kY(zL41q?`OU3CE}7kfyg!?!k@Kj)7dyqFt8?jr`AXZiU2RgON*dHWoRXMY$}X1`IvRHbu1=dV-#*3m)eSuigccWFTyk60I~ ztv}Qfty#Pqb3a!IWoMwYK?bW>uUqjWijQ-!G6=I_@kwv57-0dfT>Se+@eA9*#ZRa1;dWEG@ZoCQ>KWe zu_@4Z#?qBK{FR23UHowgu78h7pk-rSPX>a`91;p(Q|vndzCYaU1^I>$PRnzsN!QF# zNzSV}RK4Xx{_Ak+-Yy#r$S-gqCXfR(Rr?QAMSW$BRkhs(785;=(g@8S2kPE}a6G~a z#zs;yXA?>V!`?J34L2vK`a8t|m&IOTR1C0p9{RIOO2yB_2VE3Y(0_yaa&d%5#3zD4 z#a8hcjj9&T``>@JN}XgsQ8S5S_6clU+F0RQxjt#Rx|CTHS2O9-RE}7s&~wO^ADjse zJs%v+K@JIH4h}Fz*`cas{Nc+oe=_&0F#a6YmRG%8SdH<`P*-L8&G2$k*X!sFd=1#S zPzk(Qj-CU$%4o&LD_HXNrZ6_)?xK}YN9DSuS`9~#0B9@@cX_43M2N_N(2FPu#i37K z>`NDDV0UtgBhR*^pppkCAx{r?mSVah?3d1Wh0%T``j!WyCX6ZliH)*j1z?pWWRsI` z4Htj5devCm2`_&C=4g2O`Q+^Q;{5dN%H{)@ousKKP$x}vdNsV8+jnZmtP>(>WvPLn z?2KfJhNyf1Jq%>Npxx6X+$i}ru)?391^ zf4n>B_qX}q=eQilDWrSN2`9;+C)2*k4j&Cq^#+gmiQuA+Ow+bl2<%QSP3kDy>KRe* zG`ykb;m6#5%r8A9|5evQ{!ez_di0JvQfEiHs%{C1u;~1!-Tl(}Z#%oY{hclUKgVUf zARKy?>cnKTZ25lO4sOZtr5fSoD@lJ6Bx&{O6BUw_YJ!!4P%H^HHp9{t!K$C7Ab6F6 z6WeS&RlSJ~S5lo(Tfw@~T%K*ivn?f?Yl|u2nn^LS3pR-rLzFb0*%h8;KvwVO4@?%k% z1rR-LSgML>`3O~AbjqbcfJ3hmpo-`nFg^j_@4&q!DeG2loxz=RDj-1W|EpO9C_Rw} zInpCWM}up1Ac;DWBsr4g`dXiZ$gi(!jpplX3O#iCN25f+of@829>p8j!i z`T6qXm(RoVqu)MXyubR@218T46~x8wpME(#Qy`8{FKwMmS=sdiY!-vZyrg8kggfh* zvnIc!M`4>1`)JmX4s%CPoY7Tqe@@Oly}q_$`0dk&lh0@8$0wi9-hY2Qv0?a`(V$wk zc|*YWF5#y7#5~h+L}_m-ZaGU1)X~>ssc&PkI!!P?{+JL(7JRh8{<2BtZ363Q5?FO& z7ylK@Irc$wNkd^<$a^V|Vt&SJmAq4Zo~+VPj(~WF-@hsHaJivMy-8=*8G}fwQn!^X#ZdT-EMjR-@Cp2ZT$B+uA1{7(qkXC5#4P> zcN@{&Ms(MX=*|l?l}ctaE#9#Ba;&%No@islVM!RAL)2{&E3hJ69GNV0lhmIsp7^?6ykO zb&h+VJL~ZJx-I{ce;yw<5R?5TBBqOX8{{-?ajf$db<({+VT*v|8*jt9+pz8?_9blN zvriqLU2?`o>df*^H;$k`<1%9Mq&aS6gzN)86Yl*?TCYXO4i1OA51V-ixv$s@z}_-Pi|TbGExO z8+wIa^dXe%=EIvD#<0zXe>>+=t(g~pp3Jr8yRU!y+^B7W`=uqg?Z-P-nyr(^UaFL2 zrE?h9%w=oN%AEDCan`%F&iZPHL2fo*yu#^pt7MD2E9HvI!fKl--e!uQI#aydk#}{; zVk^`q$QIwS(Y_NYx;w1OA~xDLG+FHYvQLy-%8q*RDci-`=B1`NqN}cCuZmSw& zG{S#lrM?E}1MyT$ozM{b))A;aN{Hm$yxcFrWE^^g-3esp7PQ+{SE1RkySKnbz$ou- z;05H!Tsg9I{1p+uobF!gtu~acyhq{8@8kwfP@5!meYIKu?Fs|X9PCx{KYP2|{Lgb-mC$VQmzWgkDoQLL z6q%k3IV^OUpAA~@Lrs4c|5txI zlJCO>gu*Cf(`X88q#BGtwBMq(Pq_MfhlM+lAN177Pf>EqlX?aefE6&SALwa}US}O` zxQeH!+Ay0Iu8%oy_`(g}38oN=6{gU&cC0mt0>$?yN-5-+;g=ymvEUlbMNM+Gp5a+iQD$8`u8_00960MLZ#V0ImW60IS;^ A(f|Me delta 5252 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 util.MaxPathLengthWindows { + return fmt.Errorf("path length %d exceeds maximum characters: %d", pathlen, util.MaxPathLengthWindows) + } + + if pathlen > 0 && (path[0] == '\\') { + return fmt.Errorf("invalid character \\ at beginning of path: %s", path) + } + + if isUNCPathWindows(path) { + return fmt.Errorf("unsupported UNC path prefix: %s", path) + } + + if containsInvalidCharactersWindows(path) { + return fmt.Errorf("path contains invalid characters: %s", path) + } + + if !isAbsWindows(path) { + return fmt.Errorf("not an absolute Windows path: %s", path) + } + + return nil +} + +func Rmdir(path string, force bool) error { + if err := ValidatePathWindows(path); err != nil { + return err + } + + if force { + return os.RemoveAll(path) + } + return os.Remove(path) +} + +func IsMountPoint(path string) (bool, error) { + return IsSymlink(path) +} + +// IsSymlink - returns true if tgt is a mount point. +// A path is considered a mount point if: +// - directory exists and +// - it is a soft link and +// - the target path of the link exists. +// If tgt path does not exist, it returns an error +// if tgt path exists, but the source path tgt points to does not exist, it returns false without error. +func IsSymlink(tgt string) (bool, error) { + // This code is similar to k8s.io/kubernetes/pkg/util/mount except the pathExists usage. + stat, err := os.Lstat(tgt) + if err != nil { + return false, err + } + + // If its a link and it points to an existing file then its a mount point. + if stat.Mode()&os.ModeSymlink != 0 { + target, err := os.Readlink(tgt) + if err != nil { + return false, fmt.Errorf("readlink error: %v", err) + } + exists, err := pathExists(target) + if err != nil { + return false, err + } + return exists, nil + } + + return false, nil +} diff --git a/pkg/os/smb/smb.go b/pkg/os/smb/smb.go new file mode 100644 index 00000000000..5784037b652 --- /dev/null +++ b/pkg/os/smb/smb.go @@ -0,0 +1,113 @@ +//go:build windows +// +build windows + +/* +Copyright 2023 The Kubernetes 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 smb + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/kubernetes-csi/csi-driver-smb/pkg/util" + "k8s.io/klog/v2" +) + +func IsSmbMapped(remotePath string) (bool, error) { + cmdLine := `$(Get-SmbGlobalMapping -RemotePath $Env:smbremotepath -ErrorAction Stop).Status` + cmdEnv := fmt.Sprintf("smbremotepath=%s", remotePath) + out, err := util.RunPowershellCmd(cmdLine, cmdEnv) + if err != nil { + return false, fmt.Errorf("error checking smb mapping. cmd %s, output: %s, err: %v", remotePath, string(out), err) + } + + if len(out) == 0 || !strings.EqualFold(strings.TrimSpace(string(out)), "OK") { + return false, nil + } + return true, nil +} + +func NewSmbGlobalMapping(remotePath, username, password string) error { + // use PowerShell Environment Variables to store user input string to prevent command line injection + // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1 + cmdLine := fmt.Sprintf(`$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` + + `;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` + + `;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential -RequirePrivacy $true`) + + klog.V(2).Infof("begin to run NewSmbGlobalMapping with %s, %s", remotePath, username) + if output, err := util.RunPowershellCmd(cmdLine, fmt.Sprintf("smbuser=%s", username), + fmt.Sprintf("smbpassword=%s", password), + fmt.Sprintf("smbremotepath=%s", remotePath)); err != nil { + return fmt.Errorf("NewSmbGlobalMapping failed. output: %q, err: %v", string(output), err) + } + return nil +} + +func RemoveSmbGlobalMapping(remotePath string) error { + remotePath = strings.TrimSuffix(remotePath, `\`) + cmd := `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force` + klog.V(2).Infof("begin to run RemoveSmbGlobalMapping with %s", remotePath) + if output, err := util.RunPowershellCmd(cmd, fmt.Sprintf("smbremotepath=%s", remotePath)); err != nil { + return fmt.Errorf("UnmountSmbShare failed. output: %q, err: %v", string(output), err) + } + return nil +} + +// GetRemoteServerFromTarget- gets the remote server path given a mount point, the function is recursive until it find the remote server or errors out +func GetRemoteServerFromTarget(mount string) (string, error) { + target, err := os.Readlink(mount) + klog.V(2).Infof("read link for mount %s, target: %s", mount, target) + if err != nil || len(target) == 0 { + return "", fmt.Errorf("error reading link for mount %s. target %s err: %v", mount, target, err) + } + return strings.TrimSpace(target), nil +} + +// CheckForDuplicateSMBMounts checks if there is any other SMB mount exists on the same remote server +func CheckForDuplicateSMBMounts(dir, mount, remoteServer string) (bool, error) { + files, err := os.ReadDir(dir) + if err != nil { + return false, err + } + + for _, file := range files { + klog.V(6).Infof("checking file %s", file.Name()) + if file.IsDir() { + globalMountPath := filepath.Join(dir, file.Name(), "globalmount") + if strings.EqualFold(filepath.Clean(globalMountPath), filepath.Clean(mount)) { + klog.V(2).Infof("skip current mount path %s", mount) + } else { + fileInfo, err := os.Lstat(globalMountPath) + // check if the file is a symlink, if yes, check if it is pointing to the same remote server + if err == nil && fileInfo.Mode()&os.ModeSymlink != 0 { + remoteServerPath, err := GetRemoteServerFromTarget(globalMountPath) + klog.V(2).Infof("checking remote server path %s on local path %s", remoteServerPath, globalMountPath) + if err == nil { + if remoteServerPath == remoteServer { + return true, nil + } + } else { + klog.Errorf("GetRemoteServerFromTarget(%s) failed with %v", globalMountPath, err) + } + } + } + } + } + return false, err +} diff --git a/pkg/os/smb/smb_test.go b/pkg/os/smb/smb_test.go new file mode 100644 index 00000000000..98dc67406c4 --- /dev/null +++ b/pkg/os/smb/smb_test.go @@ -0,0 +1,59 @@ +//go:build windows +// +build windows + +/* +Copyright 2024 The Kubernetes 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 smb + +import ( + "fmt" + "testing" +) + +func TestCheckForDuplicateSMBMounts(t *testing.T) { + tests := []struct { + name string + dir string + mount string + remoteServer string + expectedResult bool + expectedError error + }{ + { + name: "directory does not exist", + dir: "non-existing-mount", + expectedResult: false, + expectedError: fmt.Errorf("open non-existing-mount: The system cannot find the file specified."), + }, + } + + for _, test := range tests { + result, err := CheckForDuplicateSMBMounts(test.dir, test.mount, test.remoteServer) + if result != test.expectedResult { + t.Errorf("Expected %v, got %v", test.expectedResult, result) + } + if err == nil && test.expectedError != nil { + t.Errorf("Expected error %v, got nil", test.expectedError) + } + if err != nil && test.expectedError == nil { + t.Errorf("Expected nil, got %v", err) + } + if err != nil && test.expectedError != nil && err.Error() != test.expectedError.Error() { + t.Errorf("Expected error %v, got %v", test.expectedError, err) + } + } +} diff --git a/pkg/smb/fake_mounter.go b/pkg/smb/fake_mounter.go index c874ed6a33a..c06fdd2a6a0 100644 --- a/pkg/smb/fake_mounter.go +++ b/pkg/smb/fake_mounter.go @@ -65,7 +65,7 @@ func (f *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) { func NewFakeMounter() (*mount.SafeFormatAndMount, error) { if runtime.GOOS == "windows" { - return mounter.NewSafeMounter(true) + return mounter.NewSafeMounter(true, true) } return &mount.SafeFormatAndMount{ Interface: &fakeMounter{}, diff --git a/pkg/smb/smb.go b/pkg/smb/smb.go index ffb18ca0d8e..a4e27abf061 100644 --- a/pkg/smb/smb.go +++ b/pkg/smb/smb.go @@ -73,6 +73,7 @@ type DriverOptions struct { Krb5Prefix string DefaultOnDeletePolicy string RemoveArchivedVolumePath bool + EnableWindowsHostProcess bool } // Driver implements all interfaces of CSI drivers @@ -100,6 +101,7 @@ type Driver struct { krb5Prefix string defaultOnDeletePolicy string removeArchivedVolumePath bool + enableWindowsHostProcess bool } // NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version & @@ -113,6 +115,7 @@ func NewDriver(options *DriverOptions) *Driver { driver.removeSMBMappingDuringUnmount = options.RemoveSMBMappingDuringUnmount driver.removeArchivedVolumePath = options.RemoveArchivedVolumePath driver.workingMountDir = options.WorkingMountDir + driver.enableWindowsHostProcess = options.EnableWindowsHostProcess driver.volumeLocks = newVolumeLocks() driver.krb5CacheDirectory = options.Krb5CacheDirectory @@ -146,7 +149,7 @@ func (d *Driver) Run(endpoint, _ string, testMode bool) { } klog.V(2).Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n\nStreaming logs below:", versionMeta) - d.mounter, err = mounter.NewSafeMounter(d.removeSMBMappingDuringUnmount) + d.mounter, err = mounter.NewSafeMounter(d.enableWindowsHostProcess, d.removeSMBMappingDuringUnmount) if err != nil { klog.Fatalf("Failed to get safe mounter. Error: %v", err) } diff --git a/pkg/util/util.go b/pkg/util/util.go index 06b626cb849..638e8391bf3 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -17,9 +17,17 @@ limitations under the License. package util import ( + "k8s.io/klog/v2" + "os" + "os/exec" "time" ) +const MaxPathLengthWindows = 260 + +// control the number of concurrent powershell commands running on Windows node +var powershellCmdSem = make(chan struct{}, 3) + // ExecFunc returns a exec function's output and error type ExecFunc func() (err error) @@ -46,3 +54,14 @@ func WaitUntilTimeout(timeout time.Duration, execFunc ExecFunc, timeoutFunc Time return timeoutFunc() } } + +func RunPowershellCmd(command string, envs ...string) ([]byte, error) { + // acquire a semaphore to limit the number of concurrent operations + powershellCmdSem <- struct{}{} + defer func() { <-powershellCmdSem }() + + cmd := exec.Command("powershell", "-Mta", "-NoProfile", "-Command", command) + cmd.Env = append(os.Environ(), envs...) + klog.V(6).Infof("Executing command: %q", cmd.String()) + return cmd.CombinedOutput() +}