Skip to content

Commit

Permalink
Merge pull request #10 from dell/release-1.4.0
Browse files Browse the repository at this point in the history
Release 1.4.0
  • Loading branch information
bpjain2004 authored Dec 15, 2020
2 parents e91b21b + ada1a7d commit 6757778
Show file tree
Hide file tree
Showing 59 changed files with 3,190 additions and 440 deletions.
Binary file not shown.
Binary file not shown.
15 changes: 14 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ RUN microdnf install -y \
COPY --from=builder /go/src/csi-isilon /
ENTRYPOINT ["/csi-isilon"]

# Stage to check for critical and high CVE issues via Trivy (https://github.com/aquasecurity/trivy)
# will break image build if CRITICAL issues found
# will print out all HIGH issues found
FROM driver as cvescan
# run trivy and clean up all traces after
RUN microdnf install -y --enablerepo=ubi-8-baseos tar && \
microdnf clean all && \
curl https://raw.githubusercontent.com/aquasecurity/trivy/master/contrib/install.sh | sh && \
trivy fs -s CRITICAL --exit-code 1 / && \
trivy fs -s HIGH / && \
trivy image --reset && \
rm ./bin/trivy

# final stage
# simple stage to use the driver image as the resultant image
FROM driver as final
Expand All @@ -36,7 +49,7 @@ LABEL vendor="Dell Inc." \
name="csi-isilon" \
summary="CSI Driver for Dell EMC PowerScale" \
description="CSI Driver for provisioning persistent storage from Dell EMC PowerScale" \
version="1.3.0.1" \
version="1.4.0" \
license="Apache-2.0"

COPY ./licenses /licenses
169 changes: 92 additions & 77 deletions README.md

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Release Notes - CSI Driver for PowerScale v1.4.0
[![Go Report Card](https://goreportcard.com/badge/github.com/dell/csi-isilon)](https://goreportcard.com/report/github.com/dell/csi-isilon)
[![License](https://img.shields.io/github/license/dell/csi-isilon)](https://github.com/dell/csi-isilon/blob/master/LICENSE)
[![Docker](https://img.shields.io/docker/pulls/dellemc/csi-isilon.svg?logo=docker)](https://hub.docker.com/r/dellemc/csi-isilon)

## New Features/Changes
- Added support for OpenShift 4.6 with RHEL and CoreOS worker nodes
- Added support for Red Hat Enterprise Linux (RHEL) 7.9
- Added support for Ubuntu 20.04
- Added support for Controller high availability (multiple-controllers)
- Added Topology support
- Added support for CSI Ephemeral Inline Volumes
- Added support for mount options
- Enhancements to volume creation from data source
- Enhanced support for Docker EE 3.1

## Resolved Issues
| Problem summary | Found in version | Resolved in version |
| --------------- | ---------------- | ------------------- |
| POD creation fails in OpenShift and Kubernetes environments, if hostname is not an FQDN | v1.3.0 | v1.4.0 |
| When creating volume from a snapshot or volume from volume, the owner of the new files or folders that are copied from the source snapshot is the Isilon user who is specified in secret.yaml. So the original owner of a file or folder might not be the owner of the newly created file or folder. | | v1.4.0 |

## Known Issues
| Issue | Resolution or workaround, if known |
| ----- | ---------------------------------- |
| Creating snapshot fails if the parameter IsiPath in volume snapshot class and related storage class are not the same. The driver uses the incorrect IsiPath parameter and tries to locate the source volume due to the inconsistency. | Ensure IsiPath in VolumeSnapshotClass yaml and related storageClass yaml are the same. |
| While deleting a volume, if there are files or folders created on the volume that are owned by different users. If the Isilon credentials used are for a nonprivileged Isilon user, the delete volume action fails. It is due to the limitation in Linux permission control. | To perform the delete volume action, the user account must be assigned a role that has the privilege ISI_PRIV_IFS_RESTORE. The user account must have the following set of privileges to ensure that all the CSI Isilon driver capabilities work properly:<br> * ISI_PRIV_LOGIN_PAPI<br> * ISI_PRIV_NFS<br> * ISI_PRIV_QUOTA<br> * ISI_PRIV_SNAPSHOT<br> * ISI_PRIV_IFS_RESTORE<br> * ISI_PRIV_NS_IFS_ACCESS<br> * ISI_PRIV_LOGIN_SSH<br> In some cases, ISI_PRIV_BACKUP is also required, for example, when files owned by other users have mode bits set to 700. |
| If hostname is mapped to loopback IP in /etc/hosts file, and pods are created using 1.3.0.1 release, after upgrade to 1.4.0 there is a possibility of "localhost" as stale entry in export | We recommend you not to map hostname to loopback IP in /etc/hosts file |
| If the length of the nodeID exceeds 128 characters, driver fails to update CSINode object and installation fails. This is due to a limitation set by CSI spec which doesn't allow nodeID to be greater than 128 characters. | The CSI PowerScale driver uses the hostname for building the nodeID which is set in CSINode resource object, hence we recommend not having very long hostnames in order to avoid this issue. This current limitation of 128 characters is likely to be relaxed in future kubernetes versions as per this issue in the community: https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver/issues/581
6 changes: 3 additions & 3 deletions check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fmt_count() {
}

fmt() {
gofmt -d ./service | tee $FMT_TMPFILE
gofmt -d ./service ./common/ | tee $FMT_TMPFILE
cat $FMT_TMPFILE | wc -l > $FMT_COUNT_TMPFILE
if [ ! `cat $FMT_COUNT_TMPFILE` -eq "0" ]; then
echo Found `cat $FMT_COUNT_TMPFILE` formatting issue\(s\).
Expand All @@ -30,14 +30,14 @@ FMT_RETURN_CODE=$?
echo === Finished

echo === Vetting...
go vet ${MOD_FLAGS} ./service/...
go vet ${MOD_FLAGS} ./service/... ./common/...
VET_RETURN_CODE=$?
echo === Finished

echo === Linting...
(command -v golint >/dev/null 2>&1 \
|| GO111MODULE=off go get -insecure -u golang.org/x/lint/golint) \
&& golint --set_exit_status ./service/...
&& golint --set_exit_status ./service/... ./common/...
LINT_RETURN_CODE=$?
echo === Finished

Expand Down
9 changes: 9 additions & 0 deletions common/constants/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,13 @@ const (
// DefaultIsiPath is the default isiPath which will be used if there's
// no proper isiPath value set in neither storageclass.yaml nor values.yaml
DefaultIsiPath = "/ifs"

// MaxIsiConnRetries is the max number of retries to validate connection to PowerScale Array
MaxIsiConnRetries = 10

// KubeConfig of kubernetes cluster
KubeConfig = "KUBECONFIG"

// VolumeSnapshotsPath is the snapshot directory base path on PowerScale array
VolumeSnapshotsPath = "/ifs/.snapshot"
)
6 changes: 6 additions & 0 deletions common/constants/envvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,10 @@ const (

// EnvNodeIP is the ip address of a k8s node
EnvNodeIP = "X_CSI_NODE_IP"

// EnvCustomTopologyEnabled indicates if custom topology has to be used by CSI Driver
EnvCustomTopologyEnabled = "X_CSI_CUSTOM_TOPOLOGY_ENABLED"

// EnvKubeConfigPath indicates kubernetes configuration that has to be used by CSI Driver
EnvKubeConfigPath = "KUBECONFIG"
)
54 changes: 54 additions & 0 deletions common/k8sutils/k8sutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package k8sutils

import (
"context"
"fmt"
"github.com/kubernetes-csi/csi-lib-utils/leaderelection"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"os"
)

type leaderElection interface {
Run() error
WithNamespace(namespace string)
}

// CreateKubeClientSet - Returns kubeclient set
func CreateKubeClientSet(kubeconfig string) (*kubernetes.Clientset, error) {
var clientset *kubernetes.Clientset
if kubeconfig != "" {
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, err
}
// create the clientset
clientset, err = kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
} else {
config, err := rest.InClusterConfig()
if err != nil {
return nil, err
}
// creates the clientset
clientset, err = kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
}
return clientset, nil
}

// LeaderElection - Initialize leader election
func LeaderElection(clientset *kubernetes.Clientset, lockName string, namespace string, runFunc func(ctx context.Context)) {
le := leaderelection.NewLeaderElection(clientset, lockName, runFunc)
le.WithNamespace(namespace)
if err := le.Run(); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "failed to initialize leader election: %v", err)
os.Exit(1)
}
}
15 changes: 8 additions & 7 deletions common/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ var VolumeIDPattern = regexp.MustCompile(fmt.Sprintf("^(.+)%s(\\d+)%s(.+)$", Vol
var NodeIDSeparator = "=#=#="

// NodeIDPattern is the regex pattern that identifies the NodeID
var NodeIDPattern = regexp.MustCompile(fmt.Sprintf("^(.+)%s(.+)$", NodeIDSeparator))
var NodeIDPattern = regexp.MustCompile(fmt.Sprintf("^(.+)%s(.+)%s(.+)$", NodeIDSeparator, NodeIDSeparator))

// ExportConflictMessagePattern is the regex pattern that identifies the error message of export conflict
var ExportConflictMessagePattern = regexp.MustCompile(fmt.Sprintf("^Export rules (\\d+) and (\\d+) conflict on '(.+)'$"))
Expand Down Expand Up @@ -363,17 +363,18 @@ func ParseNormalizedVolumeID(volID string) (string, int, string, error) {
return matches[1], exportID, matches[3], nil
}

// Parses NodeID to node name and IP address using pattern '^(.+)=#=#=(.+)$'
func ParseNodeID(nodeID string) (string, string, error) {
//ParseNodeID parses NodeID to node name, node FQDN and IP address using pattern '^(.+)=#=#=(.+)=#=#=(.+)'
func ParseNodeID(nodeID string) (string, string, string, error) {
matches := NodeIDPattern.FindStringSubmatch(nodeID)

if len(matches) < 3 {
return "", "", fmt.Errorf("node ID '%s' cannot match the expected '^(.+)=#=#=(.+)$' pattern", nodeID)
if len(matches) < 4 {
return "", "", "", fmt.Errorf("node ID '%s' cannot match the expected '^(.+)=#=#=(.+)=#=#=(.+)$' pattern", nodeID)
}

log.Debugf("Node ID '%s' parsed into node name '%s' and IP address '%s'", nodeID, matches[1], matches[2])
log.Debugf("Node ID '%s' parsed into node name '%s', node FQDN '%s' and IP address '%s'",
nodeID, matches[1], matches[2], matches[3])

return matches[1], matches[2], nil
return matches[1], matches[2], matches[3], nil
}

// GetPathForVolume gets the volume full path by the combination of isiPath and volumeName
Expand Down
2 changes: 2 additions & 0 deletions dell-csi-helm-installer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
images.manifest
images.tar
1 change: 1 addition & 0 deletions dell-csi-helm-installer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Installing any of the Dell EMC CSI Drivers requires a few utilities to be instal
| ------------- | ----- |
| `kubectl` | Kubectl is used to validate that the Kubernetes system meets the requirements of the driver. |
| `helm` | Helm v3 is used as the deployment tool for Charts. See, [Install HELM 3](https://helm.sh/docs/intro/install/) for instructions to install HELM 3. |
| `sshpass` | sshpass is used to check certain pre-requisities in worker nodes (in chosen drivers). |


In order to use these tools, a valid `KUBECONFIG` is required. Ensure that either a valid configuration is in the default location or that the `KUBECONFIG` environment variable points to a valid confiugration before using these tools.
Expand Down
96 changes: 91 additions & 5 deletions dell-csi-helm-installer/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,44 @@ YELLOW='\033[1;33m'
DARK_GRAY='\033[1;30m'
NC='\033[0m' # No Color

function decho() {
if [ -n "${DEBUGLOG}" ]; then
echo "$@" | tee -a "${DEBUGLOG}"
fi
}

function debuglog_only() {
if [ -n "${DEBUGLOG}" ]; then
echo "$@" >> "${DEBUGLOG}"
fi
}

function log() {
case $1 in
separator)
echo "------------------------------------------------------"
decho "------------------------------------------------------"
;;
error)
echo
decho
log separator
printf "${RED}Error: $2\n"
printf "${RED}Installation cannot continue${NC}\n"
debuglog_only "Error: $2"
debuglog_only "Installation cannot continue"
exit 1
;;
step)
printf "|\n|- %-65s" "$2"
debuglog_only "${2}"
;;
small_step)
printf "%-61s" "$2"
debuglog_only "${2}"
;;
section)
log separator
printf "> %s\n" "$2"
debuglog_only "${2}"
log separator
;;
smart_step)
Expand Down Expand Up @@ -91,7 +108,7 @@ function get_drivers() {
D="${1}"
TTT=$(pwd)
while read -r line; do
DDD=$(echo $line | awk -F '/' '{print $(NF-1)}')
DDD=$(decho $line | awk -F '/' '{print $(NF-1)}')
VALIDDRIVERS+=("$DDD")
done < <(find "${D}" -maxdepth 2 -type f -name Chart.yaml | sort)
}
Expand All @@ -104,11 +121,80 @@ function get_drivers() {
function get_release_name() {
local D="${1}"
if [ ! -z "${RELEASE}" ]; then
echo "${RELEASE}"
decho "${RELEASE}"
return
fi

local PREFIX="csi-"
R=${D#"$PREFIX"}
echo "${R}"
decho "${R}"
}

function run_command() {
local RC=0
if [ -n "${DEBUGLOG}" ]; then
local ME=$(basename "${0}")
echo "---------------" >> "${DEBUGLOG}"
echo "${ME}:${BASH_LINENO[0]} - Running command: $@" >> "${DEBUGLOG}"
debuglog_only "Results:"
eval "$@" | tee -a "${DEBUGLOG}"
RC=${PIPESTATUS[0]}
echo "---------------" >> "${DEBUGLOG}"
else
eval "$@"
RC=$?
fi
return $RC
}

# dump out information about a helm chart to the debug file
# takes a few arguments
# $1 the namespace
# $2 the release
function debuglog_helm_status() {
local NS="${1}"
local RLS="${2}"

debuglog_only "Getting information about Helm release: ${RLS}"
debuglog_only "****************"
debuglog_only "Helm Status:"
helm status "${RLS}" -n "${NS}" >> "${DEBUGLOG}"
debuglog_only "****************"
debuglog_only "Manifest"
helm get manifest "${RLS}" -n "${NS}" >> "${DEBUGLOG}"
debuglog_only "****************"
debuglog_only "Status of resources"
helm get manifest "${RLS}" -n "${NS}" | kubectl get -f - >> "${DEBUGLOG}"

}

# determines if the current KUBECONFIG is pointing to an OpenShift cluster
# echos "true" or "false"
function isOpenShift() {
# check if the securitycontextconstraints.security.openshift.io crd exists
run_command kubectl get crd | grep securitycontextconstraints.security.openshift.io --quiet >/dev/null 2>&1
local O=$?
if [[ ${O} == 0 ]]; then
# this is openshift
echo "true"
else
echo "false"
fi
}

# determines the version of OpenShift
# echos version, or empty string if not OpenShift
function OpenShiftVersion() {
# check if this is OpenShift
local O=$(isOpenShift)
if [ "${O}" == "false" ]; then
# this is not openshift
echo ""
else
local V=$(run_command kubectl get clusterversions -o jsonpath="{.items[*].status.desired.version}")
local MAJOR=$(echo "${V}" | awk -F '.' '{print $1}')
local MINOR=$(echo "${V}" | awk -F '.' '{print $2}')
echo "${MAJOR}.${MINOR}"
fi
}

Loading

0 comments on commit 6757778

Please sign in to comment.