Skip to content

Commit 19d8be4

Browse files
committed
Replace k/k checkpoint manager with file reads/writes
1 parent f314a17 commit 19d8be4

5 files changed

Lines changed: 209 additions & 82 deletions

File tree

cmd/dra-example-kubeletplugin/checkpoint.go

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,87 @@
1+
/*
2+
* Copyright The Kubernetes Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package main
218

319
import (
420
"encoding/json"
5-
6-
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
7-
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum"
21+
"fmt"
22+
"os"
23+
"path/filepath"
824

925
"sigs.k8s.io/dra-example-driver/internal/profiles"
1026
)
1127

1228
type PreparedClaims map[string]profiles.PreparedDevices
1329

1430
type Checkpoint struct {
15-
Checksum checksum.Checksum `json:"checksum"`
16-
V1 *CheckpointV1 `json:"v1,omitempty"`
31+
V1 *CheckpointV1 `json:"v1,omitempty"`
1732
}
1833

19-
var _ checkpointmanager.Checkpoint = &Checkpoint{}
20-
2134
type CheckpointV1 struct {
2235
PreparedClaims PreparedClaims `json:"preparedClaims,omitempty"`
2336
}
2437

2538
func newCheckpoint() *Checkpoint {
2639
pc := &Checkpoint{
27-
Checksum: 0,
28-
V1: &CheckpointV1{
29-
PreparedClaims: make(PreparedClaims),
30-
},
40+
V1: &CheckpointV1{},
3141
}
3242
return pc
3343
}
3444

45+
func readCheckpoint(path string) (*Checkpoint, error) {
46+
data, err := os.ReadFile(path)
47+
if err != nil {
48+
return nil, err
49+
}
50+
checkpoint := new(Checkpoint)
51+
err = json.Unmarshal(data, checkpoint)
52+
if err != nil {
53+
return nil, fmt.Errorf("unmarshal json from %s: %w", path, err)
54+
}
55+
return checkpoint, nil
56+
}
57+
58+
func writeCheckpoint(path string, checkpoint *Checkpoint) (err error) {
59+
data, err := json.Marshal(checkpoint)
60+
if err != nil {
61+
return fmt.Errorf("marshal json: %w", err)
62+
}
63+
dir := filepath.Dir(path)
64+
tmp, err := os.CreateTemp(dir, "tmp-checkpoint-*")
65+
if err != nil {
66+
return fmt.Errorf("create temp file in %s: %w", dir, err)
67+
}
68+
defer func() {
69+
if err1 := tmp.Close(); err1 != nil && err == nil {
70+
err = fmt.Errorf("close temp file: %w", err1)
71+
}
72+
}()
73+
if _, err := tmp.Write(data); err != nil {
74+
return fmt.Errorf("write to temp file %s: %w", tmp.Name(), err)
75+
}
76+
if err := tmp.Sync(); err != nil {
77+
return fmt.Errorf("sync temp file: %w", err)
78+
}
79+
if err := os.Rename(tmp.Name(), path); err != nil {
80+
return fmt.Errorf("rename %s to %s: %w", tmp.Name(), path, err)
81+
}
82+
return nil
83+
}
84+
3585
func (cp *Checkpoint) GetPreparedDevices(claimUID string) profiles.PreparedDevices {
3686
if cp.V1 == nil {
3787
return nil
@@ -47,6 +97,10 @@ func (cp *Checkpoint) AddPreparedDevices(claimUID string, pds profiles.PreparedD
4797
return
4898
}
4999

100+
if cp.V1.PreparedClaims == nil {
101+
cp.V1.PreparedClaims = make(PreparedClaims)
102+
}
103+
50104
cp.V1.PreparedClaims[claimUID] = pds
51105
}
52106

@@ -57,30 +111,3 @@ func (cp *Checkpoint) RemovePreparedDevices(claimUID string) {
57111

58112
delete(cp.V1.PreparedClaims, claimUID)
59113
}
60-
61-
func (cp *Checkpoint) MarshalCheckpoint() ([]byte, error) {
62-
cp.Checksum = 0
63-
out, err := json.Marshal(*cp)
64-
if err != nil {
65-
return nil, err
66-
}
67-
cp.Checksum = checksum.New(out)
68-
return json.Marshal(*cp)
69-
}
70-
71-
func (cp *Checkpoint) UnmarshalCheckpoint(data []byte) error {
72-
return json.Unmarshal(data, cp)
73-
}
74-
75-
func (cp *Checkpoint) VerifyChecksum() error {
76-
ck := cp.Checksum
77-
cp.Checksum = 0
78-
defer func() {
79-
cp.Checksum = ck
80-
}()
81-
out, err := json.Marshal(*cp)
82-
if err != nil {
83-
return err
84-
}
85-
return ck.Verify(out)
86-
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright The Kubernetes Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"io/fs"
21+
"os"
22+
"path/filepath"
23+
"testing"
24+
25+
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
27+
drapbv1 "k8s.io/kubelet/pkg/apis/dra/v1beta1"
28+
"tags.cncf.io/container-device-interface/pkg/cdi"
29+
"tags.cncf.io/container-device-interface/specs-go"
30+
31+
"sigs.k8s.io/dra-example-driver/internal/profiles"
32+
)
33+
34+
func TestReadWriteCheckpointRoundtrip(t *testing.T) {
35+
tests := map[string]struct {
36+
checkpoint *Checkpoint
37+
}{
38+
"new checkpoint": {
39+
checkpoint: newCheckpoint(),
40+
},
41+
"populated checkpoint": {
42+
checkpoint: &Checkpoint{
43+
V1: &CheckpointV1{
44+
PreparedClaims{
45+
"uid": profiles.PreparedDevices{
46+
{
47+
Device: drapbv1.Device{
48+
RequestNames: []string{"req"},
49+
PoolName: "pool",
50+
DeviceName: "dev",
51+
CdiDeviceIds: []string{"id"},
52+
},
53+
ContainerEdits: &cdi.ContainerEdits{
54+
ContainerEdits: &specs.ContainerEdits{
55+
Env: []string{"KEY=value"},
56+
},
57+
},
58+
AdminAccess: true,
59+
},
60+
},
61+
},
62+
},
63+
},
64+
},
65+
}
66+
67+
for name, test := range tests {
68+
t.Run(name, func(t *testing.T) {
69+
dir := t.TempDir()
70+
path := filepath.Join(dir, DriverPluginCheckpointFile)
71+
72+
checkpoint, err := readCheckpoint(path)
73+
assert.Nil(t, checkpoint)
74+
assert.ErrorIs(t, err, fs.ErrNotExist)
75+
76+
checkpoint = test.checkpoint
77+
err = writeCheckpoint(path, checkpoint)
78+
require.NoError(t, err)
79+
80+
read, err := readCheckpoint(path)
81+
require.NoError(t, err)
82+
assert.Equal(t, test.checkpoint, read)
83+
})
84+
}
85+
86+
// The checkpoint format used to contain a checksum. This test ensures that
87+
// checkpoints written in the old format can still be read.
88+
t.Run("old checkpoint format", func(t *testing.T) {
89+
dir := t.TempDir()
90+
path := filepath.Join(dir, DriverPluginCheckpointFile)
91+
92+
old := `{
93+
"checksum": 1,
94+
"v1": {
95+
"preparedClaims": {
96+
"uid": []
97+
}
98+
}
99+
}`
100+
101+
err := os.WriteFile(path, []byte(old), 0o600)
102+
require.NoError(t, err)
103+
104+
checkpoint, err := readCheckpoint(path)
105+
assert.NoError(t, err)
106+
assert.NotNil(t, checkpoint.V1.PreparedClaims)
107+
})
108+
}

0 commit comments

Comments
 (0)