Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: store system configmap in the k2d namespace #57

Merged
merged 2 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions internal/adapter/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package adapter
import (
"fmt"

"github.com/portainer/k2d/internal/adapter/types"
"github.com/portainer/k2d/internal/k8s"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -13,10 +14,21 @@ func (adapter *KubeDockerAdapter) CreateConfigMap(configMap *corev1.ConfigMap) e
return adapter.configMapStore.StoreConfigMap(configMap)
}

// CreateSystemConfigMap is a wrapper around CreateConfigMap for clarity purpose. It creates a configmap in the k2d namespace.
func (adapter *KubeDockerAdapter) CreateSystemConfigMap(configMap *corev1.ConfigMap) error {
configMap.Namespace = types.K2DNamespaceName
return adapter.configMapStore.StoreConfigMap(configMap)
}

func (adapter *KubeDockerAdapter) DeleteConfigMap(configMapName, namespace string) error {
return adapter.configMapStore.DeleteConfigMap(configMapName, namespace)
}

// DeleteSystemConfigMap is a wrapper around DeleteConfigMap for clarity purpose. It deletes a configmap from the k2d namespace.
func (adapter *KubeDockerAdapter) DeleteSystemConfigMap(configMapName string) error {
return adapter.configMapStore.DeleteConfigMap(configMapName, types.K2DNamespaceName)
}

func (adapter *KubeDockerAdapter) GetConfigMap(configMapName, namespace string) (*corev1.ConfigMap, error) {
configMap, err := adapter.configMapStore.GetConfigMap(configMapName, namespace)
if err != nil {
Expand All @@ -40,6 +52,11 @@ func (adapter *KubeDockerAdapter) GetConfigMap(configMapName, namespace string)
return &versionedConfigMap, nil
}

// GetSystemConfigMap is a wrapper around GetConfigMap for clarity purpose. It retrieves a configmap from the k2d namespace.
func (adapter *KubeDockerAdapter) GetSystemConfigMap(configMapName string) (*corev1.ConfigMap, error) {
return adapter.GetConfigMap(configMapName, types.K2DNamespaceName)
}

func (adapter *KubeDockerAdapter) GetConfigMapTable(namespace string) (*metav1.Table, error) {
configMapList, err := adapter.listConfigMaps(namespace)
if err != nil {
Expand Down Expand Up @@ -70,6 +87,11 @@ func (adapter *KubeDockerAdapter) ListConfigMaps(namespace string) (corev1.Confi
return versionedConfigMapList, nil
}

// ListSystemConfigMaps is a wrapper around ListConfigMaps for clarity purpose. It lists configmaps from the k2d namespace.
func (adapter *KubeDockerAdapter) ListSystemConfigMaps() (corev1.ConfigMapList, error) {
return adapter.ListConfigMaps(types.K2DNamespaceName)
}

func (adapter *KubeDockerAdapter) listConfigMaps(namespace string) (core.ConfigMapList, error) {
return adapter.configMapStore.GetConfigMaps(namespace)
}
2 changes: 1 addition & 1 deletion internal/adapter/container_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func (adapter *KubeDockerAdapter) createContainerFromPodSpec(ctx context.Context
return fmt.Errorf("unable to marshal internal pod spec: %w", err)
}
options.labels[k2dtypes.PodLastAppliedConfigLabelKey] = string(internalPodSpecData)
options.labels[k2dtypes.NamespaceLabelKey] = options.namespace
options.labels[k2dtypes.NamespaceNameLabelKey] = options.namespace
options.labels[k2dtypes.WorkloadNameLabelKey] = options.containerName
options.labels[k2dtypes.NetworkNameLabelKey] = naming.BuildNetworkName(options.namespace)

Expand Down
14 changes: 4 additions & 10 deletions internal/adapter/converter/persistentvolume.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"k8s.io/kubernetes/pkg/apis/core"
)

func (converter *DockerAPIConverter) ConvertVolumeToPersistentVolume(volume volume.Volume) (*core.PersistentVolume, error) {
func (converter *DockerAPIConverter) ConvertVolumeToPersistentVolume(volume volume.Volume, boundToPersistentVolumeClaim bool) (*core.PersistentVolume, error) {
creationDate, err := time.Parse(time.RFC3339, volume.CreatedAt)
if err != nil {
return nil, fmt.Errorf("unable to parse volume creation date: %w", err)
Expand All @@ -19,17 +19,11 @@ func (converter *DockerAPIConverter) ConvertVolumeToPersistentVolume(volume volu
phase := core.VolumeBound
persistentVolumeClaimReference := &core.ObjectReference{
Kind: "PersistentVolumeClaim",
Namespace: volume.Labels[k2dtypes.NamespaceLabelKey],
Name: volume.Labels[k2dtypes.PersistentVolumeClaimLabelKey],
Namespace: volume.Labels[k2dtypes.NamespaceNameLabelKey],
Name: volume.Labels[k2dtypes.PersistentVolumeClaimNameLabelKey],
}

configMap, err := converter.configMapStore.GetConfigMap(volume.Labels[k2dtypes.PersistentVolumeClaimLabelKey], volume.Labels[k2dtypes.NamespaceLabelKey])
if err != nil {
// how to make this logged as an info
fmt.Printf("unable to retrieve config map for volume %s: %s\n. Setting the phase to released and no claim reference", volume.Name, err)
}

if configMap == nil {
if !boundToPersistentVolumeClaim {
phase = core.VolumeReleased
persistentVolumeClaimReference = nil
}
Expand Down
7 changes: 4 additions & 3 deletions internal/adapter/converter/persistentvolumeclaim.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

func (converter *DockerAPIConverter) UpdateConfigMapToPersistentVolumeClaim(persistentVolumeClaim *core.PersistentVolumeClaim, configMap *corev1.ConfigMap) error {
// TODO: this should be reviewed, there should be a single way to retrieve information from the store interface
// creation-timestamp can be obtained from a label or directly from the metadata, based on how the K2D_STORE_BACKEND is set
creationDate := ""
if configMap.Labels["store.k2d.io/filesystem/creation-timestamp"] != "" {
Expand All @@ -33,8 +34,8 @@ func (converter *DockerAPIConverter) UpdateConfigMapToPersistentVolumeClaim(pers
}

persistentVolumeClaim.ObjectMeta = metav1.ObjectMeta{
Name: configMap.Labels[k2dtypes.PersistentVolumeClaimLabelKey],
Namespace: configMap.Labels[k2dtypes.NamespaceLabelKey],
Name: configMap.Labels[k2dtypes.PersistentVolumeClaimNameLabelKey],
Namespace: configMap.Labels[k2dtypes.NamespaceNameLabelKey],
CreationTimestamp: metav1.Time{
Time: timeConvertedCreationDate,
},
Expand All @@ -45,7 +46,7 @@ func (converter *DockerAPIConverter) UpdateConfigMapToPersistentVolumeClaim(pers

persistentVolumeClaim.Spec = core.PersistentVolumeClaimSpec{
StorageClassName: &storageClassName,
VolumeName: naming.BuildPersistentVolumeName(configMap.Labels[k2dtypes.PersistentVolumeClaimLabelKey], configMap.Labels[k2dtypes.NamespaceLabelKey]),
VolumeName: naming.BuildPersistentVolumeName(configMap.Labels[k2dtypes.PersistentVolumeClaimNameLabelKey], configMap.Labels[k2dtypes.NamespaceNameLabelKey]),
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
},
Expand Down
19 changes: 11 additions & 8 deletions internal/adapter/converter/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (converter *DockerAPIConverter) ConvertContainerToPod(container types.Conta
ObjectMeta: metav1.ObjectMeta{
Name: containerName,
CreationTimestamp: metav1.NewTime(time.Unix(container.Created, 0)),
Namespace: container.Labels[k2dtypes.NamespaceLabelKey],
Namespace: container.Labels[k2dtypes.NamespaceNameLabelKey],
Annotations: map[string]string{
"kubectl.kubernetes.io/last-applied-configuration": container.Labels[k2dtypes.WorkloadLastAppliedConfigLabelKey],
},
Expand Down Expand Up @@ -241,6 +241,7 @@ func (converter *DockerAPIConverter) setResourceRequirements(hostConfig *contain
//
// It returns an error if any occurred fetching the Secret or obtaining the bind mappings fails.
func (converter *DockerAPIConverter) SetServiceAccountTokenAndCACert(hostConfig *container.HostConfig) error {
// TODO: this is also a system resource
secret, err := converter.secretStore.GetSecret(k2dtypes.K2dServiceAccountSecretName, k2dtypes.K2DNamespaceName)
if err != nil {
return fmt.Errorf("unable to get secret %s: %w", k2dtypes.K2dServiceAccountSecretName, err)
Expand Down Expand Up @@ -473,27 +474,29 @@ func (converter *DockerAPIConverter) setVolumeMounts(namespace string, hostConfi
}

// handleVolumeSource configures the Docker host configuration's volume bindings based on a Kubernetes VolumeSource.
// The VolumeSource can be of type ConfigMap, Secret, or HostPath.
//
// For PersistentVolumeClaim:
// The function uses the volume name and namespace to generate a unique name for the volume.
// The VolumeSource can be of type ConfigMap, Secret, HostPath, or PersistentVolumeClaim.
//
// Parameters:
// - namespace: The Kubernetes namespace where the volume resources (ConfigMap or Secret) are located.
// - namespace: The Kubernetes namespace where the volume resources (ConfigMap, Secret, or PersistentVolumeClaim) are located.
// - hostConfig: A pointer to the Docker host configuration to which the volume bindings will be appended.
// - volume: A Kubernetes Volume object describing the source of the volume.
// - volumeMount: A Kubernetes VolumeMount object containing additional specifications for mounting the volume.
//
// Behavior:
// - For ConfigMap and Secret:
// 1. Retrieves the resource (ConfigMap or Secret) from the store based on the volume source.
//
// 1. Retrieves the resource (ConfigMap or Secret) from the corresponding store based on the volume source.
//
// 2. Utilizes the store's specific implementation to generate a list of filesystem binds.
//
// 3. Appends these binds to the 'Binds' field of the Docker host configuration.
// - For HostPath:
// Directly appends a bind between the HostPath and the volume mount path to the Docker host configuration.
// - For PersistentVolumeClaim:
// Utilizes the volume name and namespace to generate the volume name and appends a bind to the Docker host configuration.
//
// Returns:
// - An error if retrieval of the ConfigMap or Secret fails or if bind generation encounters issues.
// - An error if the retrieval of the ConfigMap, Secret, or PersistentVolumeClaim fails, or if bind generation encounters issues.
// - Nil if the volume bindings are successfully appended to the Docker host configuration.
func (converter *DockerAPIConverter) handleVolumeSource(namespace string, hostConfig *container.HostConfig, volume core.Volume, volumeMount core.VolumeMount) error {
if volume.VolumeSource.ConfigMap != nil {
Expand Down
19 changes: 10 additions & 9 deletions internal/adapter/filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func AllDeployments(namespace string) filters.Args {
// filter := AllNamespaces()
// // Now 'filter' can be used in Docker API calls to filter resources that are labeled with any Kubernetes namespace.
func AllNamespaces() filters.Args {
return filters.NewArgs(filters.Arg("label", types.NamespaceLabelKey))
return filters.NewArgs(filters.Arg("label", types.NamespaceNameLabelKey))
}

// AllServices creates a Docker filter argument to target all Docker resources labeled as services within a specific Kubernetes namespace.
Expand Down Expand Up @@ -98,7 +98,7 @@ func ByNamespace(namespace string) filters.Args {
if namespace == "" {
return AllNamespaces()
}
return filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", types.NamespaceLabelKey, namespace)))
return filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", types.NamespaceNameLabelKey, namespace)))
}

// ByPod creates a Docker filter argument to target a specific pod within a specific Kubernetes namespace.
Expand All @@ -116,7 +116,7 @@ func ByNamespace(namespace string) filters.Args {
// // Now 'filter' can be used in Docker API calls to filter resources of pod 'mypod' in the 'default' Kubernetes namespace.
func ByPod(namespace, podName string) filters.Args {
filter := filters.NewArgs()
filter.Add("label", fmt.Sprintf("%s=%s", types.NamespaceLabelKey, namespace))
filter.Add("label", fmt.Sprintf("%s=%s", types.NamespaceNameLabelKey, namespace))
filter.Add("label", fmt.Sprintf("%s=%s", types.WorkloadNameLabelKey, podName))
return filter
}
Expand All @@ -137,22 +137,23 @@ func ByPod(namespace, podName string) filters.Args {
func ByService(namespace, serviceName string) filters.Args {
filter := filters.NewArgs()
filter.Add("label", fmt.Sprintf("%s=%s", types.ServiceNameLabelKey, serviceName))
filter.Add("label", fmt.Sprintf("%s=%s", types.NamespaceLabelKey, namespace))
filter.Add("label", fmt.Sprintf("%s=%s", types.NamespaceNameLabelKey, namespace))
return filter
}

// AllPersistentVolumes creates a Docker filter argument to list persistent volumes if the PersistentVolumeLabelKey exists.
// AllPersistentVolumes creates a Docker filter argument that targets resources labeled with a Kubernetes persistent volume name.
// This function uses the types.PersistentVolumeNameLabelKey constant as the base label key to filter Docker resources.
//
// Parameters:
// - None.
// - None
//
// Returns:
// - filters.Args: A Docker filter object to be used in Docker API calls to filter persistent volumes.
// - filters.Args: A Docker filter object that can be used to filter Docker API calls based on the presence of the persistent volume name label.
//
// Usage Example:
//
// filter := AllPersistentVolumes()
// // Now 'filter' can be used in Docker API calls to filter persistent volumes.
// // Now 'filter' can be used in Docker API calls to filter resources that are labeled with any Kubernetes persistent volume name.
func AllPersistentVolumes() filters.Args {
return filters.NewArgs(filters.Arg("label", types.PersistentVolumeLabelKey))
return filters.NewArgs(filters.Arg("label", types.PersistentVolumeNameLabelKey))
}
4 changes: 2 additions & 2 deletions internal/adapter/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (adapter *KubeDockerAdapter) CreateNetworkFromNamespace(ctx context.Context
networkOptions := types.NetworkCreate{
Driver: "bridge",
Labels: map[string]string{
k2dtypes.NamespaceLabelKey: namespace.Name,
k2dtypes.NamespaceNameLabelKey: namespace.Name,
k2dtypes.NamespaceLastAppliedConfigLabelKey: lastAppliedConfiguration,
},
Options: map[string]string{
Expand Down Expand Up @@ -166,7 +166,7 @@ func (adapter *KubeDockerAdapter) listNamespaces(ctx context.Context) (core.Name
namespaceList := []core.Namespace{}

for _, network := range networks {
namespace := network.Labels[k2dtypes.NamespaceLabelKey]
namespace := network.Labels[k2dtypes.NamespaceNameLabelKey]
namespaceList = append(namespaceList, adapter.converter.ConvertNetworkToNamespace(namespace, network))
}

Expand Down
8 changes: 7 additions & 1 deletion internal/adapter/naming/naming.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ func BuildNetworkName(namespace string) string {
return fmt.Sprintf("k2d-%s", namespace)
}

// Each persistentVolume is named using the following format:
// Each volume is named using the following format:
// k2d-pv-[namespace]-[volume-name]
func BuildPersistentVolumeName(volumeName string, namespace string) string {
return fmt.Sprintf("k2d-pv-%s-%s", namespace, volumeName)
}

// Each system configmap associated to a PVC is named using the following format:
// pvc-[namespace]-[pvc-name]
func BuildPVCSystemConfigMapName(persistentVolumeClaimName, namespace string) string {
return fmt.Sprintf("pvc-%s-%s", namespace, persistentVolumeClaimName)
}
39 changes: 37 additions & 2 deletions internal/adapter/persistentvolume.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/docker/docker/errdefs"
adaptererr "github.com/portainer/k2d/internal/adapter/errors"
"github.com/portainer/k2d/internal/adapter/filters"
"github.com/portainer/k2d/internal/adapter/naming"
k2dtypes "github.com/portainer/k2d/internal/adapter/types"
"github.com/portainer/k2d/internal/k8s"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -32,7 +34,18 @@ func (adapter *KubeDockerAdapter) GetPersistentVolume(ctx context.Context, persi
return nil, fmt.Errorf("unable to inspect docker volume %s: %w", persistentVolumeName, err)
}

persistentVolume, err := adapter.converter.ConvertVolumeToPersistentVolume(volume)
boundToPersistentVolumeClaim := true
pvcName := naming.BuildPVCSystemConfigMapName(volume.Labels[k2dtypes.PersistentVolumeClaimNameLabelKey], volume.Labels[k2dtypes.NamespaceNameLabelKey])
_, err = adapter.GetSystemConfigMap(pvcName)
if err != nil {
adapter.logger.Debugw("unable to retrieve system configmap for volume, setting phase to released and no claim reference",
"volume", volume.Name,
"error", err,
)
boundToPersistentVolumeClaim = false
}

persistentVolume, err := adapter.converter.ConvertVolumeToPersistentVolume(volume, boundToPersistentVolumeClaim)
if err != nil {
return nil, fmt.Errorf("unable to convert Docker volume to PersistentVolume: %w", err)
}
Expand Down Expand Up @@ -90,15 +103,37 @@ func (adapter *KubeDockerAdapter) listPersistentVolumes(ctx context.Context) (co
return core.PersistentVolumeList{}, fmt.Errorf("unable to list volumes to return the output values from a Docker volume: %w", err)
}

configMaps, err := adapter.ListSystemConfigMaps()
if err != nil {
return core.PersistentVolumeList{}, fmt.Errorf("unable to list system configmaps: %w", err)
}

persistentVolumes := []core.PersistentVolume{}

for _, volume := range volumeList.Volumes {
persistentVolume, err := adapter.converter.ConvertVolumeToPersistentVolume(*volume)
boundToPersistentVolumeClaim := false

for _, configMap := range configMaps.Items {
if configMap.Labels[k2dtypes.PersistentVolumeNameLabelKey] == volume.Name {
boundToPersistentVolumeClaim = true
break
}
}

if !boundToPersistentVolumeClaim {
adapter.logger.Debugw("unable to retrieve system configmap for volume, setting phase to released and no claim reference",
"volume", volume.Name,
"error", err,
)
}

persistentVolume, err := adapter.converter.ConvertVolumeToPersistentVolume(*volume, boundToPersistentVolumeClaim)
if err != nil {
return core.PersistentVolumeList{}, fmt.Errorf("unable to convert Docker volume to PersistentVolume: %w", err)
}

persistentVolumes = append(persistentVolumes, *persistentVolume)

}

return core.PersistentVolumeList{
Expand Down
Loading