Change:
When kubelet is creating configmap volume for a pod, it finished writing dir and made symlink to . At the same time, the kubelet is restarted, unfortunately the configmap item symlink is not created. The restarted kubelet will try to configmap, . Kubelet should create absent symlink for configmap volume. can hack kubelet to help reproduce it: sleep some time before ` kubelet and start official kubelet will skip to create symlink for UserVisibleFiles No response_ <details</details<details</details<details</details<details</details<details</details<details</details
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/sig node
/sig storage This seems like an issue for sig storage. Can someone take a look and confirm?
Can you please add the version details as well?
/remove-sig node
/assign

Discussion:
//     portion of the payload was deleted and is still present on disk. // //  4. The data in the current timestamped directory is compared to the projected <del> //     data to determine if an update is required. </del> <ins> //     data to determine if an update to data directory is required. </ins> // <del> //  5. A new timestamped dir is created. </del> <ins> //  5. A new timestamped dir is created if an update is required. </ins> // //  6. The payload is written to the new timestamped directory. //

oldTsPath := filepath.Join(w.targetDir, oldTsDir) var pathsToRemove sets.String <ins> shouldWrite := true </ins> // if there was no old version, there's nothing to remove if len(oldTsDir) != 0 { // (3)

klog.Errorf("%s: error determining whether payload should be written to disk: %v", w.logContext, err) return err } else if !should && len(pathsToRemove) == 0 { <del> klog.V(4).Infof("%s: no update required for target directory %v", w.logContext, w.targetDir) return nil </del> <ins> klog.V(4).Infof("%s: write not required for data directory %v", w.logContext, oldTsDir) // data directory is already up to date, but we need to make sure that // the user-visible symlinks are created. // See https://github.com/kubernetes/kubernetes/issues/121472 for more details. // Reset oldTsDir to empty string to avoid removing the data directory. shouldWrite = false oldTsDir = "" </ins> } else { klog.V(4).Infof("%s: write required for target directory %v", w.logContext, w.targetDir) } } <del> // (5) tsDir, err := w.newTimestampDir() if err != nil { klog.V(4).Infof("%s: error creating new ts data directory: %v", w.logContext, err) return err } tsDirName := filepath.Base(tsDir) // (6) if err = w.writePayloadToDir(cleanPayload, tsDir); err != nil { klog.Errorf("%s: error writing payload to ts data directory %s: %v", w.logContext, tsDir, err) return err } klog.V(4).Infof("%s: performed write of new data to ts data directory: %s", w.logContext, tsDir) </del> <ins> if shouldWrite { // (5) tsDir, err := w.newTimestampDir() if err != nil { klog.V(4).Infof("%s: error creating new ts data directory: %v", w.logContext, err) return err } tsDirName := filepath.Base(tsDir) </ins> <del> // (7) if setPerms != nil { if err := setPerms(tsDirName); err != nil { klog.Errorf("%s: error applying ownership settings: %v", w.logContext, err) </del> <ins> // (6) if err = w.writePayloadToDir(cleanPayload, tsDir); err != nil { klog.Errorf("%s: error writing payload to ts data directory %s: %v", w.logContext, tsDir, err) </ins> return err } <del> } </del> <ins> klog.V(4).Infof("%s: performed write of new data to ts data directory: %s", w.logContext, tsDir) </ins> <del> // (8) newDataDirPath := filepath.Join(w.targetDir, newDataDirName) if err = os.Symlink(tsDirName, newDataDirPath); err != nil { os.RemoveAll(tsDir) klog.Errorf("%s: error creating symbolic link for atomic update: %v", w.logContext, err) return err } </del> <ins> // (7) if setPerms != nil { if err := setPerms(tsDirName); err != nil { klog.Errorf("%s: error applying ownership settings: %v", w.logContext, err) return err } } </ins> <del> // (9) if runtime.GOOS == "windows" { os.Remove(dataDirPath) err = os.Symlink(tsDirName, dataDirPath) os.Remove(newDataDirPath) } else { err = os.Rename(newDataDirPath, dataDirPath) } if err != nil { os.Remove(newDataDirPath) os.RemoveAll(tsDir) klog.Errorf("%s: error renaming symbolic link for data directory %s: %v", w.logContext, newDataDirPath, err) return err </del> <ins> // (8) newDataDirPath := filepath.Join(w.targetDir, newDataDirName) if err = os.Symlink(tsDirName, newDataDirPath); err != nil { if err := os.RemoveAll(tsDir); err != nil { klog.Errorf("%s: error removing new ts directory %s: %v", w.logContext, tsDir, err) } klog.Errorf("%s: error creating symbolic link for atomic update: %v", w.logContext, err) return err } // (9) if runtime.GOOS == "windows" { if err := os.Remove(dataDirPath); err != nil { klog.Errorf("%s: error removing data dir directory %s: %v", w.logContext, dataDirPath, err) } err = os.Symlink(tsDirName, dataDirPath) if err := os.Remove(newDataDirPath); err != nil { klog.Errorf("%s: error removing new data dir directory %s: %v", w.logContext, newDataDirPath, err) } } else { err = os.Rename(newDataDirPath, dataDirPath) } if err != nil { if err := os.Remove(newDataDirPath); err != nil && err != os.ErrNotExist { klog.Errorf("%s: error removing new data dir directory %s: %v", w.logContext, newDataDirPath, err) } if err := os.RemoveAll(tsDir); err != nil { klog.Errorf("%s: error removing new ts directory %s: %v", w.logContext, tsDir, err) } klog.Errorf("%s: error renaming symbolic link for data directory %s: %v", w.logContext, newDataDirPath, err) return err } </ins> } // (10)

t.Fatalf("unexpected error while writing: %v", err) } } <ins> func TestWriteAgainAfterUnexpectedExit(t *testing.T) { testCases := []struct { name       string payload    map[string]FileProjection simulateFn func(targetDir string, payload map[string]FileProjection) error }{ { name: "process killed before creating user visible files", payload: map[string]FileProjection{ "foo": {Mode: 0644, Data: []byte("foo")}, "bar": {Mode: 0644, Data: []byte("bar")}, }, simulateFn: func(targetDir string, payload map[string]FileProjection) error { for filename := range payload { path := filepath.Join(targetDir, filename) if err := os.RemoveAll(path); err != nil { return err } } return nil }, }, } for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { targetDir, err := utiltesting.MkTmpdir("atomic-write") if err != nil { t.Fatalf("unexpected error creating tmp dir: %v", err) } defer func() { err := os.RemoveAll(targetDir) if err != nil { t.Errorf("%v: unexpected error removing tmp dir: %v", tc.name, err) } }() writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"} err = writer.Write(tc.payload, nil) if err != nil { t.Fatalf("unexpected error writing payload: %v", err) } err = tc.simulateFn(targetDir, tc.payload) if err != nil { t.Fatalf("failed to simulate the unexpected exit: %v", err) } err = writer.Write(tc.payload, nil) if err != nil { t.Fatalf("unexpected error writing payload again: %v", err) } checkVolumeContents(targetDir, tc.name, tc.payload, t) }) } } </ins>