aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/bindings/network/network.go10
-rw-r--r--pkg/bindings/network/types.go3
-rw-r--r--pkg/bindings/network/types_prune_options.go16
-rw-r--r--pkg/bindings/test/networks_test.go47
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/play.go8
-rw-r--r--pkg/domain/entities/system.go5
-rw-r--r--pkg/domain/infra/abi/generate.go172
-rw-r--r--pkg/domain/infra/abi/play.go105
-rw-r--r--pkg/domain/infra/abi/system.go26
-rw-r--r--pkg/domain/infra/tunnel/network.go3
-rw-r--r--pkg/domain/infra/tunnel/system.go2
-rw-r--r--pkg/util/kube.go16
13 files changed, 347 insertions, 68 deletions
diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go
index 6f3aa8594..17451c273 100644
--- a/pkg/bindings/network/network.go
+++ b/pkg/bindings/network/network.go
@@ -184,7 +184,13 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool,
// Prune removes unused CNI networks
func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPruneReport, error) {
- // TODO Filters is not implemented
+ if options == nil {
+ options = new(PruneOptions)
+ }
+ params, err := options.ToParams()
+ if err != nil {
+ return nil, err
+ }
var (
prunedNetworks []*entities.NetworkPruneReport
)
@@ -193,7 +199,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPrune
return nil, err
}
- response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", nil, nil)
+ response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", params, nil)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/network/types.go b/pkg/bindings/network/types.go
index 47dce67c7..e62ae8f52 100644
--- a/pkg/bindings/network/types.go
+++ b/pkg/bindings/network/types.go
@@ -79,4 +79,7 @@ type ExistsOptions struct {
// PruneOptions are optional options for removing unused
// CNI networks
type PruneOptions struct {
+ // Filters are applied to the prune of networks to be more
+ // specific on choosing
+ Filters map[string][]string
}
diff --git a/pkg/bindings/network/types_prune_options.go b/pkg/bindings/network/types_prune_options.go
index 84e1a8b32..f17e09d69 100644
--- a/pkg/bindings/network/types_prune_options.go
+++ b/pkg/bindings/network/types_prune_options.go
@@ -19,3 +19,19 @@ func (o *PruneOptions) Changed(fieldName string) bool {
func (o *PruneOptions) ToParams() (url.Values, error) {
return util.ToParams(o)
}
+
+// WithFilters
+func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions {
+ v := value
+ o.Filters = v
+ return o
+}
+
+// GetFilters
+func (o *PruneOptions) GetFilters() map[string][]string {
+ var filters map[string][]string
+ if o.Filters == nil {
+ return filters
+ }
+ return o.Filters
+}
diff --git a/pkg/bindings/test/networks_test.go b/pkg/bindings/test/networks_test.go
index ef20235ae..df7d7cd1c 100644
--- a/pkg/bindings/test/networks_test.go
+++ b/pkg/bindings/test/networks_test.go
@@ -37,6 +37,53 @@ var _ = Describe("Podman networks", func() {
bt.cleanup()
})
+ It("podman prune unused networks with filters", func() {
+ name := "foobar"
+ opts := network.CreateOptions{
+ Name: &name,
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+
+ // Invalid filters should return error
+ filtersIncorrect := map[string][]string{
+ "status": {"dummy"},
+ }
+ _, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filtersIncorrect))
+ Expect(err).ToNot(BeNil())
+
+ // List filter params should not work with prune.
+ filtersIncorrect = map[string][]string{
+ "name": {name},
+ }
+ _, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filtersIncorrect))
+ Expect(err).ToNot(BeNil())
+
+ // Mismatched label, correct filter params => no network should be pruned.
+ filtersIncorrect = map[string][]string{
+ "label": {"xyz"},
+ }
+ pruneResponse, err := network.Prune(connText, new(network.PruneOptions).WithFilters(filtersIncorrect))
+ Expect(err).To(BeNil())
+ Expect(len(pruneResponse)).To(Equal(0))
+
+ // Mismatched until, correct filter params => no network should be pruned.
+ filters := map[string][]string{
+ "until": {"50"}, // January 1, 1970
+ }
+ pruneResponse, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filters))
+ Expect(err).To(BeNil())
+ Expect(len(pruneResponse)).To(Equal(0))
+
+ // Valid filter params => network should be pruned now.
+ filters = map[string][]string{
+ "until": {"5000000000"}, //June 11, 2128
+ }
+ pruneResponse, err = network.Prune(connText, new(network.PruneOptions).WithFilters(filters))
+ Expect(err).To(BeNil())
+ Expect(len(pruneResponse)).To(Equal(1))
+ })
+
It("create network", func() {
// create a network with blank config should work
_, err = network.Create(connText, &network.CreateOptions{})
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index bcab617af..f695d32fd 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -88,7 +88,7 @@ type ContainerEngine interface {
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
Shutdown(ctx context.Context)
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
- Unshare(ctx context.Context, args []string) error
+ Unshare(ctx context.Context, args []string, options SystemUnshareOptions) error
Version(ctx context.Context) (*SystemVersionReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
VolumeExists(ctx context.Context, namesOrID string) (*BoolReport, error)
diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go
index 6883fe6c5..cd8bb9506 100644
--- a/pkg/domain/entities/play.go
+++ b/pkg/domain/entities/play.go
@@ -45,8 +45,16 @@ type PlayKubePod struct {
ContainerErrors []string
}
+// PlayKubeVolume represents a single volume created by play kube.
+type PlayKubeVolume struct {
+ // Name - Name of the volume created by play kube.
+ Name string
+}
+
// PlayKubeReport contains the results of running play kube.
type PlayKubeReport struct {
// Pods - pods created by play kube.
Pods []PlayKubePod
+ // Volumes - volumes created by play kube.
+ Volumes []PlayKubeVolume
}
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index 1a671d59e..31a6185dc 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -98,6 +98,11 @@ type SystemVersionReport struct {
Server *define.Version `json:",omitempty"`
}
+// SystemUnshareOptions describes the options for the unshare command
+type SystemUnshareOptions struct {
+ RootlessCNI bool
+}
+
type ComponentVersion struct {
types.Version
}
diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go
index 94f649e15..b0853b554 100644
--- a/pkg/domain/infra/abi/generate.go
+++ b/pkg/domain/infra/abi/generate.go
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
+ "strings"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
@@ -43,120 +44,174 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
var (
- pods []*libpod.Pod
- ctrs []*libpod.Container
- kubePods []*k8sAPI.Pod
- kubeServices []k8sAPI.Service
- content []byte
+ pods []*libpod.Pod
+ ctrs []*libpod.Container
+ vols []*libpod.Volume
+ podContent [][]byte
+ content [][]byte
)
+
+ // Lookup for podman objects.
for _, nameOrID := range nameOrIDs {
- // Get the container in question
+ // Let's assume it's a container, so get the container.
ctr, err := ic.Libpod.LookupContainer(nameOrID)
if err != nil {
- pod, err := ic.Libpod.LookupPod(nameOrID)
- if err != nil {
+ if !strings.Contains(err.Error(), "no such container") {
return nil, err
}
- pods = append(pods, pod)
} else {
if len(ctr.Dependencies()) > 0 {
return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies")
}
- // we cannot deal with ctrs already in a pod
+ // we cannot deal with ctrs already in a pod.
if len(ctr.PodID()) > 0 {
return nil, errors.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID())
}
ctrs = append(ctrs, ctr)
+ continue
+ }
+
+ // Maybe it's a pod.
+ pod, err := ic.Libpod.LookupPod(nameOrID)
+ if err != nil {
+ if !strings.Contains(err.Error(), "no such pod") {
+ return nil, err
+ }
+ } else {
+ pods = append(pods, pod)
+ continue
+ }
+
+ // Or volume.
+ vol, err := ic.Libpod.LookupVolume(nameOrID)
+ if err != nil {
+ if !strings.Contains(err.Error(), "no such volume") {
+ return nil, err
+ }
+ } else {
+ vols = append(vols, vol)
+ continue
}
+
+ // If it reaches here is because the name or id did not exist.
+ return nil, errors.Errorf("Name or ID %q not found", nameOrID)
}
- // check our inputs
- if len(pods) > 0 && len(ctrs) > 0 {
- return nil, errors.New("cannot generate pods and containers at the same time")
+ // Generate kube persistent volume claims from volumes.
+ if len(vols) >= 1 {
+ pvs, err := getKubePVCs(vols)
+ if err != nil {
+ return nil, err
+ }
+
+ content = append(content, pvs...)
}
+ // Generate kube pods and services from pods.
if len(pods) >= 1 {
pos, svcs, err := getKubePods(pods, options.Service)
if err != nil {
return nil, err
}
- kubePods = append(kubePods, pos...)
+ podContent = append(podContent, pos...)
if options.Service {
- kubeServices = append(kubeServices, svcs...)
+ content = append(content, svcs...)
}
- } else {
+ }
+
+ // Generate the kube pods from containers.
+ if len(ctrs) >= 1 {
po, err := libpod.GenerateForKube(ctrs)
if err != nil {
return nil, err
}
- kubePods = append(kubePods, po)
+ b, err := generateKubeYAML(po)
+ if err != nil {
+ return nil, err
+ }
+
+ podContent = append(podContent, b)
if options.Service {
- kubeServices = append(kubeServices, libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{}))
+ b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{}))
+ if err != nil {
+ return nil, err
+ }
+ content = append(content, b)
}
}
- content, err := generateKubeOutput(kubePods, kubeServices, options.Service)
+ // Content order is based on helm install order (secret, persistentVolumeClaim, service, pod).
+ content = append(content, podContent...)
+
+ // Generate kube YAML file from all kube kinds.
+ k, err := generateKubeOutput(content)
if err != nil {
return nil, err
}
- return &entities.GenerateKubeReport{Reader: bytes.NewReader(content)}, nil
+ return &entities.GenerateKubeReport{Reader: bytes.NewReader(k)}, nil
}
-func getKubePods(pods []*libpod.Pod, getService bool) ([]*k8sAPI.Pod, []k8sAPI.Service, error) {
- kubePods := make([]*k8sAPI.Pod, 0)
- kubeServices := make([]k8sAPI.Service, 0)
+// getKubePods returns kube pod and service YAML files from podman pods.
+func getKubePods(pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) {
+ pos := [][]byte{}
+ svcs := [][]byte{}
for _, p := range pods {
- po, svc, err := p.GenerateForKube()
+ po, sp, err := p.GenerateForKube()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ b, err := generateKubeYAML(po)
if err != nil {
return nil, nil, err
}
+ pos = append(pos, b)
- kubePods = append(kubePods, po)
if getService {
- kubeServices = append(kubeServices, libpod.GenerateKubeServiceFromV1Pod(po, svc))
+ b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, sp))
+ if err != nil {
+ return nil, nil, err
+ }
+ svcs = append(svcs, b)
}
}
- return kubePods, kubeServices, nil
+ return pos, svcs, nil
}
-func generateKubeOutput(kubePods []*k8sAPI.Pod, kubeServices []k8sAPI.Service, hasService bool) ([]byte, error) {
- output := make([]byte, 0)
- marshalledPods := make([]byte, 0)
- marshalledServices := make([]byte, 0)
+// getKubePVCs returns kube persistent volume claim YAML files from podman volumes.
+func getKubePVCs(volumes []*libpod.Volume) ([][]byte, error) {
+ pvs := [][]byte{}
- for i, p := range kubePods {
- if i != 0 {
- marshalledPods = append(marshalledPods, []byte("---\n")...)
- }
-
- b, err := yaml.Marshal(p)
+ for _, v := range volumes {
+ b, err := generateKubeYAML(v.GenerateForKube())
if err != nil {
return nil, err
}
-
- marshalledPods = append(marshalledPods, b...)
+ pvs = append(pvs, b)
}
- if hasService {
- for i, s := range kubeServices {
- if i != 0 {
- marshalledServices = append(marshalledServices, []byte("---\n")...)
- }
-
- b, err := yaml.Marshal(s)
- if err != nil {
- return nil, err
- }
+ return pvs, nil
+}
- marshalledServices = append(marshalledServices, b...)
- }
+// generateKubeYAML marshalls a kube kind into a YAML file.
+func generateKubeYAML(kubeKind interface{}) ([]byte, error) {
+ b, err := yaml.Marshal(kubeKind)
+ if err != nil {
+ return nil, err
}
+ return b, nil
+}
+
+// generateKubeOutput generates kube YAML file containing multiple kube kinds.
+func generateKubeOutput(content [][]byte) ([]byte, error) {
+ output := make([]byte, 0)
+
header := `# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
@@ -169,13 +224,18 @@ func generateKubeOutput(kubePods []*k8sAPI.Pod, kubeServices []k8sAPI.Service, h
return nil, err
}
+ // Add header to kube YAML file.
output = append(output, []byte(fmt.Sprintf(header, podmanVersion.Version))...)
- // kube generate order is based on helm install order (service, pod...)
- if hasService {
- output = append(output, marshalledServices...)
- output = append(output, []byte("---\n")...)
+
+ // kube generate order is based on helm install order (secret, persistentVolume, service, pod...).
+ // Add kube kinds.
+ for i, b := range content {
+ if i != 0 {
+ output = append(output, []byte("---\n")...)
+ }
+
+ output = append(output, b...)
}
- output = append(output, marshalledPods...)
return output, nil
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 3b5c141d7..52f759f13 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -7,6 +7,7 @@ import (
"io"
"io/ioutil"
"os"
+ "strconv"
"strings"
"github.com/containers/common/pkg/secrets"
@@ -43,6 +44,12 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, err
}
+ // sort kube kinds
+ documentList, err = sortKubeKinds(documentList)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
+ }
+
// create pod on each document if it is a pod or deployment
// any other kube kind will be skipped
for _, document := range documentList {
@@ -84,6 +91,20 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
report.Pods = append(report.Pods, r.Pods...)
validKinds++
+ case "PersistentVolumeClaim":
+ var pvcYAML v1.PersistentVolumeClaim
+
+ if err := yaml.Unmarshal(document, &pvcYAML); err != nil {
+ return nil, errors.Wrapf(err, "unable to read YAML %q as Kube PersistentVolumeClaim", path)
+ }
+
+ r, err := ic.playKubePVC(ctx, &pvcYAML, options)
+ if err != nil {
+ return nil, err
+ }
+
+ report.Volumes = append(report.Volumes, r.Volumes...)
+ validKinds++
default:
logrus.Infof("kube kind %s not supported", kind)
continue
@@ -313,6 +334,68 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
return &report, nil
}
+// playKubePVC creates a podman volume from a kube persistent volume claim.
+func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.PersistentVolumeClaim, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+ var report entities.PlayKubeReport
+ opts := make(map[string]string)
+
+ // Get pvc name.
+ // This is the only required pvc attribute to create a podman volume.
+ name := pvcYAML.GetName()
+ if strings.TrimSpace(name) == "" {
+ return nil, fmt.Errorf("persistent volume claim name can not be empty")
+ }
+
+ // Create podman volume options.
+ volOptions := []libpod.VolumeCreateOption{
+ libpod.WithVolumeName(name),
+ libpod.WithVolumeLabels(pvcYAML.GetLabels()),
+ }
+
+ // Get pvc annotations and create remaining podman volume options if available.
+ // These are podman volume options that do not match any of the persistent volume claim
+ // attributes, so they can be configured using annotations since they will not affect k8s.
+ for k, v := range pvcYAML.GetAnnotations() {
+ switch k {
+ case util.VolumeDriverAnnotation:
+ volOptions = append(volOptions, libpod.WithVolumeDriver(v))
+ case util.VolumeDeviceAnnotation:
+ opts["device"] = v
+ case util.VolumeTypeAnnotation:
+ opts["type"] = v
+ case util.VolumeUIDAnnotation:
+ uid, err := strconv.Atoi(v)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert uid %s to integer", v)
+ }
+ volOptions = append(volOptions, libpod.WithVolumeUID(uid))
+ opts["UID"] = v
+ case util.VolumeGIDAnnotation:
+ gid, err := strconv.Atoi(v)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert gid %s to integer", v)
+ }
+ volOptions = append(volOptions, libpod.WithVolumeGID(gid))
+ opts["GID"] = v
+ case util.VolumeMountOptsAnnotation:
+ opts["o"] = v
+ }
+ }
+ volOptions = append(volOptions, libpod.WithVolumeOptions(opts))
+
+ // Create volume.
+ vol, err := ic.Libpod.NewVolume(ctx, volOptions...)
+ if err != nil {
+ return nil, err
+ }
+
+ report.Volumes = append(report.Volumes, entities.PlayKubeVolume{
+ Name: vol.Name(),
+ })
+
+ return &report, nil
+}
+
// readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag
func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) {
var cm v1.ConfigMap
@@ -374,3 +457,25 @@ func getKubeKind(obj []byte) (string, error) {
return kubeObject.Kind, nil
}
+
+// sortKubeKinds adds the correct creation order for the kube kinds.
+// Any pod dependecy will be created first like volumes, secrets, etc.
+func sortKubeKinds(documentList [][]byte) ([][]byte, error) {
+ var sortedDocumentList [][]byte
+
+ for _, document := range documentList {
+ kind, err := getKubeKind(document)
+ if err != nil {
+ return nil, err
+ }
+
+ switch kind {
+ case "Pod", "Deployment":
+ sortedDocumentList = append(sortedDocumentList, document)
+ default:
+ sortedDocumentList = append([][]byte{document}, sortedDocumentList...)
+ }
+ }
+
+ return sortedDocumentList, nil
+}
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index a3e753384..f87f9e370 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -390,13 +390,25 @@ func unshareEnv(graphroot, runroot string) []string {
fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot))
}
-func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
+func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
+ unshare := func() error {
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+ }
+
+ if options.RootlessCNI {
+ rootlesscni, err := ic.Libpod.GetRootlessCNINetNs(true)
+ if err != nil {
+ return err
+ }
+ defer rootlesscni.Cleanup(ic.Libpod)
+ return rootlesscni.Do(unshare)
+ }
+ return unshare()
}
func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) {
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index adf34460c..7e59e44c2 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -92,5 +92,6 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
// Network prune removes unused cni networks
func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
- return network.Prune(ic.ClientCtx, nil)
+ opts := new(network.PruneOptions).WithFilters(options.Filters)
+ return network.Prune(ic.ClientCtx, opts)
}
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index d2c5063c9..7400d3771 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -28,7 +28,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
return system.DiskUsage(ic.ClientCtx, nil)
}
-func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
+func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
return errors.New("unshare is not supported on remote clients")
}
diff --git a/pkg/util/kube.go b/pkg/util/kube.go
new file mode 100644
index 000000000..1255cdfc5
--- /dev/null
+++ b/pkg/util/kube.go
@@ -0,0 +1,16 @@
+package util
+
+const (
+ // Kube annotation for podman volume driver.
+ VolumeDriverAnnotation = "volume.podman.io/driver"
+ // Kube annotation for podman volume type.
+ VolumeTypeAnnotation = "volume.podman.io/type"
+ // Kube annotation for podman volume device.
+ VolumeDeviceAnnotation = "volume.podman.io/device"
+ // Kube annotation for podman volume UID.
+ VolumeUIDAnnotation = "volume.podman.io/uid"
+ // Kube annotation for podman volume GID.
+ VolumeGIDAnnotation = "volume.podman.io/gid"
+ // Kube annotation for podman volume mount options.
+ VolumeMountOptsAnnotation = "volume.podman.io/mount-options"
+)