diff --git a/internal/postgres/config.go b/internal/postgres/config.go index 0300d4d34..75371e6af 100644 --- a/internal/postgres/config.go +++ b/internal/postgres/config.go @@ -298,13 +298,24 @@ func reloadCommand( // descriptor gets closed and reopened to use the builtin `[ -nt` to check // mtimes. // - https://unix.stackexchange.com/a/407383 + // + // In the manageAutogrowAnnotation function below, df is used to return the + // relevant volume size in Mebibytes. The 'read' variable gets the value from + // the '1M-blocks' output (second column) and the 'use' variable gets the value + // from the 'Use%' column (fifth column). This value is grabbed after stripping + // out the column headers (before the '\n') and then getting the respective + // value delimited by the white spaces by using the 'read -r' command. + // The underscores (_) discard fields and the variables store them. This allows + // for selective parsing of the provided lines. The percent value is stripped of + // the '%' and then used to determine if a expansion should be triggered by + // setting the calculated volume size using the 'size' variable. script := fmt.Sprintf(` # Parameters for curl when managing autogrow annotation. APISERVER="https://kubernetes.default.svc" SERVICEACCOUNT="/var/run/secrets/kubernetes.io/serviceaccount" -NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace) -TOKEN=$(cat ${SERVICEACCOUNT}/token) -CACERT=${SERVICEACCOUNT}/ca.crt +NAMESPACE=$(cat "${SERVICEACCOUNT}/namespace") +TOKEN=$(cat "${SERVICEACCOUNT}/token") +CACERT="${SERVICEACCOUNT}/ca.crt" # Manage autogrow annotation. # Return size in Mebibytes. @@ -313,27 +324,29 @@ manageAutogrowAnnotation() { local trigger=$2 local maxGrow=$3 - size=$(df --block-size=M /"${volume}" | awk 'FNR == 2 {print $2}') - use=$(df /"${volume}" | awk 'FNR == 2 {print $5}') + size=$(df --block-size=M /"${volume}") + read -r _ size _ <<< "${size#*$'\n'}" + use=$(df /"${volume}") + read -r _ _ _ _ use _ <<< "${use#*$'\n'}" sizeInt="${size//M/}" # Use the sed punctuation class, because the shell will not accept the percent sign in an expansion. - useInt=$(echo $use | sed 's/[[:punct:]]//g') + useInt=${use//[[:punct:]]/} triggerExpansion="$((useInt > trigger))" - if [[ $triggerExpansion -eq 1 ]]; then + if [[ ${triggerExpansion} -eq 1 ]]; then newSize="$(((sizeInt / 2)+sizeInt))" # Only compare with maxGrow if it is set (not empty) - if [[ -n "$maxGrow" ]]; then + if [[ -n "${maxGrow}" ]]; then # check to see how much we would normally grow sizeDiff=$((newSize - sizeInt)) # Compare the size difference to the maxGrow; if it is greater, cap it to maxGrow - if [[ $sizeDiff -gt $maxGrow ]]; then + if [[ ${sizeDiff} -gt ${maxGrow} ]]; then newSize=$((sizeInt + maxGrow)) fi fi newSizeMi="${newSize}Mi" - d='[{"op": "add", "path": "/metadata/annotations/suggested-'"${volume}"'-pvc-size", "value": "'"$newSizeMi"'"}]' - curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -XPATCH "${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods/${HOSTNAME}?fieldManager=kubectl-annotate" -H "Content-Type: application/json-patch+json" --data "$d" + d='[{"op": "add", "path": "/metadata/annotations/suggested-'"${volume}"'-pvc-size", "value": "'"${newSizeMi}"'"}]' + curl --cacert "${CACERT}" --header "Authorization: Bearer ${TOKEN}" -XPATCH "${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods/${HOSTNAME}?fieldManager=kubectl-annotate" -H "Content-Type: application/json-patch+json" --data "${d}" fi } diff --git a/internal/postgres/config_test.go b/internal/postgres/config_test.go index 53bcfc1bf..124596933 100644 --- a/internal/postgres/config_test.go +++ b/internal/postgres/config_test.go @@ -18,9 +18,11 @@ import ( "time" "gotest.tools/v3/assert" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" + "github.com/crunchydata/postgres-operator/internal/initialize" "github.com/crunchydata/postgres-operator/internal/testing/cmp" "github.com/crunchydata/postgres-operator/internal/testing/require" "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" @@ -600,3 +602,44 @@ EOF chmod +x /tmp/pg_rewind_tde.sh`)) }) } + +func TestReloadCommand(t *testing.T) { + shellcheck := require.ShellCheck(t) + + pgdataSize := resource.MustParse("1Gi") + pgwalSize := resource.MustParse("2Gi") + + command := reloadCommand( + "some-name", + &v1beta1.VolumeClaimSpecWithAutoGrow{ + AutoGrow: &v1beta1.AutoGrowSpec{ + Trigger: initialize.Int32(10), + MaxGrow: &pgdataSize, + }, + }, + &v1beta1.VolumeClaimSpecWithAutoGrow{ + AutoGrow: &v1beta1.AutoGrowSpec{ + Trigger: initialize.Int32(20), + MaxGrow: &pgwalSize, + }, + }, + ) + + // Expect a bash command with an inline script. + assert.DeepEqual(t, command[:3], []string{"bash", "-ceu", "--"}) + assert.Assert(t, len(command) > 3) + + // Write out that inline script. + dir := t.TempDir() + file := filepath.Join(dir, "script.bash") + assert.NilError(t, os.WriteFile(file, []byte(command[3]), 0o600)) + + // Expect shellcheck to be happy. + cmd := exec.CommandContext(t.Context(), shellcheck, "--enable=all", file) + output, err := cmd.CombinedOutput() + assert.NilError(t, err, "%q\n%s", cmd.Args, output) + + assert.Assert(t, cmp.Contains(command[3], "manageAutogrowAnnotation \"pgdata\" \"10\" \"1024\"")) + assert.Assert(t, cmp.Contains(command[3], "manageAutogrowAnnotation \"pgwal\" \"20\" \"2048\"")) + +} diff --git a/internal/postgres/reconcile_test.go b/internal/postgres/reconcile_test.go index a72672824..73ac1125d 100644 --- a/internal/postgres/reconcile_test.go +++ b/internal/postgres/reconcile_test.go @@ -173,9 +173,9 @@ containers: # Parameters for curl when managing autogrow annotation. APISERVER="https://kubernetes.default.svc" SERVICEACCOUNT="/var/run/secrets/kubernetes.io/serviceaccount" - NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace) - TOKEN=$(cat ${SERVICEACCOUNT}/token) - CACERT=${SERVICEACCOUNT}/ca.crt + NAMESPACE=$(cat "${SERVICEACCOUNT}/namespace") + TOKEN=$(cat "${SERVICEACCOUNT}/token") + CACERT="${SERVICEACCOUNT}/ca.crt" # Manage autogrow annotation. # Return size in Mebibytes. @@ -184,27 +184,29 @@ containers: local trigger=$2 local maxGrow=$3 - size=$(df --block-size=M /"${volume}" | awk 'FNR == 2 {print $2}') - use=$(df /"${volume}" | awk 'FNR == 2 {print $5}') + size=$(df --block-size=M /"${volume}") + read -r _ size _ <<< "${size#*$'\n'}" + use=$(df /"${volume}") + read -r _ _ _ _ use _ <<< "${use#*$'\n'}" sizeInt="${size//M/}" # Use the sed punctuation class, because the shell will not accept the percent sign in an expansion. - useInt=$(echo $use | sed 's/[[:punct:]]//g') + useInt=${use//[[:punct:]]/} triggerExpansion="$((useInt > trigger))" - if [[ $triggerExpansion -eq 1 ]]; then + if [[ ${triggerExpansion} -eq 1 ]]; then newSize="$(((sizeInt / 2)+sizeInt))" # Only compare with maxGrow if it is set (not empty) - if [[ -n "$maxGrow" ]]; then + if [[ -n "${maxGrow}" ]]; then # check to see how much we would normally grow sizeDiff=$((newSize - sizeInt)) # Compare the size difference to the maxGrow; if it is greater, cap it to maxGrow - if [[ $sizeDiff -gt $maxGrow ]]; then + if [[ ${sizeDiff} -gt ${maxGrow} ]]; then newSize=$((sizeInt + maxGrow)) fi fi newSizeMi="${newSize}Mi" - d='[{"op": "add", "path": "/metadata/annotations/suggested-'"${volume}"'-pvc-size", "value": "'"$newSizeMi"'"}]' - curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -XPATCH "${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods/${HOSTNAME}?fieldManager=kubectl-annotate" -H "Content-Type: application/json-patch+json" --data "$d" + d='[{"op": "add", "path": "/metadata/annotations/suggested-'"${volume}"'-pvc-size", "value": "'"${newSizeMi}"'"}]' + curl --cacert "${CACERT}" --header "Authorization: Bearer ${TOKEN}" -XPATCH "${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods/${HOSTNAME}?fieldManager=kubectl-annotate" -H "Content-Type: application/json-patch+json" --data "${d}" fi }