diff --git a/go.mod b/go.mod index d2e138810..2ece6f572 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/k8snetworkplumbingwg/sriovnet v1.2.1-0.20240128120937-3ca5e43034e6 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.33.1 + github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 github.com/vishvananda/netlink v1.2.1-beta.2 diff --git a/go.sum b/go.sum index 976aa5583..1fbc3f327 100644 --- a/go.sum +++ b/go.sum @@ -232,6 +232,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb h1:1xSVPOd7/UA+39/hXEGnBJ13p6JFB0E1EvQFlrRDOXI= diff --git a/pkg/cdi/cdi.go b/pkg/cdi/cdi.go index eb7d06fea..8ed934cf8 100644 --- a/pkg/cdi/cdi.go +++ b/pkg/cdi/cdi.go @@ -39,6 +39,10 @@ type CDI interface { // impl implements CDI interface type impl struct { + // lastGeneratedName is the last computed file name to which the CDI spec + // was written. This is used to remove the spec file when the resource pool + // is removed or updated. + lastGeneratedName string } // New returns an instance of CDI interface implementation @@ -48,9 +52,9 @@ func New() CDI { // CreateCDISpecForPool creates CDI spec file with specified devices func (c *impl) CreateCDISpecForPool(resourcePrefix string, rPool types.ResourcePool) error { - err := c.CleanupSpecs() + err := c.removeOldSpec() if err != nil { - glog.Errorf("CreateCDISpecForPool(): can not cleanup old spec files: %v", err) + glog.Errorf("CreateCDISpecForPool(): can not cleanup old spec file: %v", err) return err } cdiDevices := make([]cdiSpecs.Device, 0) @@ -80,12 +84,22 @@ func (c *impl) CreateCDISpecForPool(resourcePrefix string, rPool types.ResourceP cdiSpec.Devices = append(cdiSpec.Devices, device) } - err = cdi.GetRegistry().SpecDB().WriteSpec(&cdiSpec, cdiSpecPrefix+resourcePrefix) + // calculate hash of the cdiSpec + digest := Digest(cdiSpec) + name, err := cdi.GenerateNameForTransientSpec(&cdiSpec, digest.String()[0:12]) + if err != nil { + glog.Errorf("CreateCDISpecForPool(): can not generate transient name: %v", err) + return err + } + + err = cdi.GetRegistry().SpecDB().WriteSpec(&cdiSpec, cdiSpecPrefix+resourcePrefix+"-"+name) if err != nil { glog.Errorf("CreateCDISpecForPool(): can not create CDI json: %v", err) return err } + // save the last generated name to remove the spec file later + c.lastGeneratedName = cdiSpecPrefix + resourcePrefix + "-" + name return nil } @@ -111,6 +125,14 @@ func (c *impl) CreateContainerAnnotations(devicesIDs []string, resourcePrefix, r return annotations, nil } +func (c *impl) removeOldSpec() error { + if c.lastGeneratedName == "" { + return nil + } + registry := cdi.GetRegistry() + return registry.SpecDB().RemoveSpec(c.lastGeneratedName) +} + // CleanupSpecs removes previously-created CDI specs func (c *impl) CleanupSpecs() error { for _, dir := range cdi.GetRegistry().GetSpecDirectories() { diff --git a/pkg/cdi/digest.go b/pkg/cdi/digest.go new file mode 100644 index 000000000..dad7792d6 --- /dev/null +++ b/pkg/cdi/digest.go @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 cdi + +import ( + "encoding/json" + + cdiSpecs "github.com/container-orchestrated-devices/container-device-interface/specs-go" + "github.com/opencontainers/go-digest" +) + +// Digest returns the digest of the given CDI spec. +func Digest(spec cdiSpecs.Spec) digest.Digest { + digester := digest.Canonical.Digester() + enc := json.NewEncoder(digester.Hash()) + if err := enc.Encode(spec); err != nil { + return "" + } + return digester.Digest() +} diff --git a/pkg/cdi/digest_test.go b/pkg/cdi/digest_test.go new file mode 100644 index 000000000..42e973463 --- /dev/null +++ b/pkg/cdi/digest_test.go @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 cdi_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/container-orchestrated-devices/container-device-interface/specs-go" + "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/cdi" +) + +var _ = Describe("Digest", func() { + Context("successfully create digest", func() { + It("should return digest", func() { + spec := specs.Spec{ + Version: specs.CurrentVersion, + Kind: "nvidia.com/net-pci", + Devices: []specs.Device{ + { + ContainerEdits: specs.ContainerEdits{ + DeviceNodes: []*specs.DeviceNode{ + { + Path: "/dev/infiniband/issm0", + HostPath: "/dev/infiniband/issm0", + Permissions: "rw", + }, + }, + }, + }, + }, + } + got := cdi.Digest(spec) + Expect(got.String()).To(Equal("sha256:250959bdd6c927d5eb06860bdbbd974011c3c26f06980aef3c0621a7c027140d")) + }) + }) +})