hetznercloud / cli

A command-line interface for Hetzner Cloud
MIT License
1.06k stars 78 forks source link

issue with creating context in interactive mode (hcloud: context create is an interactive command) #819

Open jnovermars opened 1 month ago

jnovermars commented 1 month ago

TL;DR

not able to use HCLOUD_TOKEN while running hcloud create context

Expected behavior

when running HCLOUD_TOKEN="x" hcloud context create dummy this should work and use the token provided in the environment var.

Observed behavior

i get the error:

hcloud: context create is an interactive command

Minimal working example

we use this in our gitlab ci pipeline where hcloud is installed via brew brew install hcloud:

HCLOUD_TOKEN="${TF_VAR_hcloud_token}" hcloud context create gitlab-ci <<<"Y"

Log output

$ HCLOUD_TOKEN="${TF_VAR_hcloud_token}" hcloud context create gitlab-ci <<<"Y"
hcloud: context create is an interactive command
Cleaning up project directory and file based variables 00:01
ERROR: Job failed: exit code 1

Additional information

See commend as there the links are working

jnovermars commented 1 month ago

While checking the issue and diving into the code i found the following, keep in mind i am not sure if this is the case and is just an assumption :)

While this already happened before and saw it was fixed in the past already multiple times i think it has been broken by the last update by @phm07 with the change from if !ui.StdoutIsTerminal() { to if !s.Terminal().StdoutIsTerminal() { in commit: https://github.com/hetznercloud/cli/commit/0db50460b1c88f99b0fb05b87d525a5bd91d140d

I am not a go developer so this is just a suggestion and i see that it has been changed what could of course explain why? Is it not a better idea to skip the interactive check when the os environment variable is provided?

apricote commented 1 month ago

Hey @jnovermars,

I can not reproduce the issue on the latest version or on main. I do use Linux though. You are running your gitlab-ci pipeline on macOS?

What is the output of the following commands?

For reproducing I used the following command:

$ HCLOUD_CONFIG=hcloud-cli-819.toml HCLOUD_TOKEN=$(openssl rand -hex 64 | head -c 64) bash -c "hcloud context create foo <<<'Y'"
The HCLOUD_TOKEN environment variable is set. Do you want to use the token from HCLOUD_TOKEN for the new context? (Y/n): Context foo created and activated

Its also not necessary to create a context in your CI pipeline, you can do everything with environment variables and or flags if you want to.

jnovermars commented 1 month ago

It is easy to reproduce it in docker, i runned the commands you asked:

docker build -t test-hcloud . && docker run test-hcloud bash -c "hcloud version && which hcloud && echo \$0"

hcloud version: hcloud v1.45.0
which hcloud: /home/linuxbrew/.linuxbrew/bin/hcloud
echo $0: bash

i quicky created a small version of our docker file, and you are totally right that for CI pipeline the commands could run without the context. But i found this and think this is a bug as the expectation is that it could run without TTY but still it is required.

I converted your command to run it in docker:

docker build -t test-hcloud . && docker run test-hcloud bash -c "HCLOUD_CONFIG=hcloud-cli-819.toml HCLOUD_TOKEN=\$(openssl rand -hex 64 | head -c 64) hcloud context create foo <<<'Y'"

The docker file: Big disclaimer, this is a brutally stripped docker file and just for demo purposes to run the hcloud command

FROM ubuntu:24.04

ENV TZ="Europe/Amsterdam"
ENV DEBIAN_FRONTEND="noninteractive"

# Install updates and dependencies
RUN apt update -y && apt install --no-install-recommends -y -q \
        curl \
        git \
        nano \
        build-essential \
        wget \
        dirmngr \
        apt-transport-https  \
        gnupg \
        lsb-release  \
        ca-certificates \
        software-properties-common \
    && apt-get clean

## second stage: Brew
ENV HOMEBREW_NO_AUTO_UPDATE=1

# Install brew
RUN apt-get update && \
    apt-get install -y -q --allow-unauthenticated \
    git \
    sudo
RUN useradd -m -s /bin/zsh linuxbrew && \
    usermod -aG sudo linuxbrew &&  \
    mkdir -p /home/linuxbrew/.linuxbrew && \
    chown -R linuxbrew: /home/linuxbrew/.linuxbrew
USER linuxbrew
RUN /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

USER root
RUN chown -R $CONTAINER_USER: /home/linuxbrew/.linuxbrew
ENV PATH="/home/linuxbrew/.linuxbrew/bin:${PATH}"

USER linuxbrew
RUN brew update
RUN brew install gcc

USER root

## Setup linuxbrew user
RUN mkdir -p /home/linuxbrew/workspace
WORKDIR /home/linuxbrew/workspace

USER linuxbrew

## third stage: install packages

## Install Terraform/OpenTofu gitlab scripts
USER root
RUN apt update -y && apt install --no-install-recommends -y -q \
        idn2 \
        openssh-client \
    && apt-get clean

USER linuxbrew

## Install Packer, kubectl, hcloud, jq

RUN brew install hcloud
jooola commented 1 month ago

I agree that we should support non-interactive mode, I think have an "assume yes" to bypass any yes/no prompt would be helpful in this scenario. But this might require a broader change across the CLI to support a such a global flag.

jooola commented 1 month ago

This is currently low priority, as one can always use the HCLOUD_TOKEN environment variable when running a script. Using a context for a script seem to be a really specific use case.

jnovermars commented 1 month ago

That is indeed the workaround we use now

phm07 commented 2 weeks ago

Creating a context in non-interactive mode would mean that you would have to pass the token as a command argument. This is potentially unsafe and bad practice, since the token would then be stored in your shell history, even if you deleted the context. (Edit: Getting it from the environment, like you suggested, would be viable though. We would still need an extra flag then)

As a workaround, you can manually create contexts by manually appending to ~/.config/hcloud/cli.toml. The config is structured in such a way that appending a context does not break it.