Repository: code-ready/crc
Pull request #2098 (2021-03-15 10:18:28)

[Doc] Remove `as=system:admin` for developer user
With https://github.com/code-ready/snc/pull/361, developer user
is not part of sudoers cluster role. This patch remove `as=system:admin`
mentions for developer user.

fixes: #2097


openshift-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/2098#" title="Author self-approved">praveenkumar</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=code-ready%2Fcrc).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[docs/OWNERS](https://github.com/code-ready/crc/blob/master/docs/OWNERS)~~ [praveenkumar]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
praveenkumar: /assign @robin-owen 

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 937511936..9cbad1733 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -69,6 +69,9 @@ jobs:
       - run:
           name: mock make release
           command: make BUNDLE_DIR=/tmp MOCK_BUNDLE=true release
+      - run:
+          name: make rpm
+          command: CONTAINER_RUNTIME=docker make test-rpmbuild
 
   build_docs:
     machine:
diff --git a/Makefile b/Makefile
index f3eacacff..c08272988 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 SHELL := /bin/bash
 
-BUNDLE_VERSION = 4.7.1
+BUNDLE_VERSION = 4.7.2
 BUNDLE_EXTENSION = crcbundle
 CRC_VERSION = 1.23.1
 COMMIT_SHA=$(shell git rev-parse --short HEAD)
@@ -59,7 +59,7 @@ ifdef OKD_VERSION
 endif
 
 # https://golang.org/cmd/link/
-LDFLAGS := $(VERSION_VARIABLES) -extldflags='-static' -s -w
+LDFLAGS := $(VERSION_VARIABLES) -extldflags='-static' ${GO_EXTRA_LDFLAGS}
 
 # Add default target
 .PHONY: default
@@ -82,19 +82,19 @@ check: cross build_e2e $(HOST_BUILD_DIR)/crc-embedder test cross-lint vendorchec
 
 .PHONY: install
 install: $(SOURCES)
-	go install -ldflags="$(LDFLAGS)" ./cmd/crc
+	go install -ldflags="$(LDFLAGS)" $(GO_EXTRA_BUILDFLAGS) ./cmd/crc
 
 $(BUILD_DIR)/macos-amd64/crc: $(SOURCES)
-	GOARCH=amd64 GOOS=darwin go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/macos-amd64/crc ./cmd/crc
+	GOARCH=amd64 GOOS=darwin go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/macos-amd64/crc $(GO_EXTRA_BUILDFLAGS) ./cmd/crc
 
 $(BUILD_DIR)/linux-amd64/crc: $(SOURCES)
-	GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/linux-amd64/crc ./cmd/crc
+	GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/linux-amd64/crc $(GO_EXTRA_BUILDFLAGS) ./cmd/crc
 
 $(BUILD_DIR)/windows-amd64/crc.exe: $(SOURCES)
-	GOARCH=amd64 GOOS=windows go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/windows-amd64/crc.exe ./cmd/crc
+	GOARCH=amd64 GOOS=windows go build -ldflags="$(LDFLAGS)" -o $(BUILD_DIR)/windows-amd64/crc.exe $(GO_EXTRA_BUILDFLAGS) ./cmd/crc
 
 $(HOST_BUILD_DIR)/crc-embedder: $(SOURCES)
-	go build --tags="build" -ldflags="$(LDFLAGS)" -o $(HOST_BUILD_DIR)/crc-embedder ./cmd/crc-embedder
+	go build --tags="build" -ldflags="$(LDFLAGS)" -o $(HOST_BUILD_DIR)/crc-embedder $(GO_EXTRA_BUILDFLAGS) ./cmd/crc-embedder
 
 .PHONY: cross ## Cross compiles all binaries
 cross: $(BUILD_DIR)/macos-amd64/crc $(BUILD_DIR)/linux-amd64/crc $(BUILD_DIR)/windows-amd64/crc.exe
@@ -111,6 +111,14 @@ containerized: clean
 test:
 	go test -race --tags build -v -ldflags="$(VERSION_VARIABLES)" ./pkg/... ./cmd/...
 
+.PHONY: spec test-rpmbuild
+
+GENERATED_RPM_FILES=packaging/rpm/crc.spec images/rpmbuild/Containerfile
+spec: $(GENERATED_RPM_FILES)
+	
+test-rpmbuild: spec
+	${CONTAINER_RUNTIME} build -f images/rpmbuild/Containerfile .
+
 .PHONY: build_docs
 build_docs:
 	${CONTAINER_RUNTIME} run -v $(CURDIR)/docs:/docs:Z --rm $(DOCS_BUILD_CONTAINER) build_docs -b html5 -D /$(DOCS_BUILD_DIR) -o index.html $(DOCS_BUILD_TARGET)
@@ -139,6 +147,7 @@ clean_macos_package:
 
 .PHONY: clean ## Remove all build artifacts
 clean: clean_docs clean_macos_package
+	rm -f $(GENERATED_RPM_FILES)
 	rm -rf $(BUILD_DIR)
 	rm -f $(GOPATH)/bin/crc
 	rm -rf $(RELEASE_DIR)
@@ -180,7 +189,7 @@ fmt:
 	@gofmt -l -w $(SOURCE_DIRS)
 
 $(GOPATH)/bin/golangci-lint:
-	pushd /tmp && GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.36.0 && popd
+	pushd /tmp && GO111MODULE=on go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.37.1 && popd
 
 # Run golangci-lint against code
 .PHONY: lint cross-lint
@@ -266,3 +275,18 @@ $(BUILD_DIR)/macos-amd64/crc-macos-amd64.pkg: packagedir
 
 $(BUILD_DIR)/macos-amd64/crc-installer.tar: packagedir
 	tar -cvf $(BUILD_DIR)/macos-amd64/crc-installer.tar ./packaging
+
+$(GOPATH)/bin/gomod2rpmdeps:
+	pushd /tmp && GO111MODULE=on go get github.com/cfergeau/gomod2rpmdeps/cmd/gomod2rpmdeps && popd
+
+%.spec: %.spec.in $(GOPATH)/bin/gomod2rpmdeps
+	@$(GOPATH)/bin/gomod2rpmdeps | sed -e '/__BUNDLED_REQUIRES__/r /dev/stdin' \
+					   -e '/__BUNDLED_REQUIRES__/d' \
+					   -e 's/__VERSION__/'$(CRC_VERSION)'/g' \
+					   -e 's/__OPENSHIFT_VERSION__/'$(BUNDLE_VERSION)'/g' \
+				       $< >$@
+
+%: %.in
+	@sed -e 's/__VERSION__/'$(CRC_VERSION)'/g' \
+	     -e 's/__OPENSHIFT_VERSION__/'$(BUNDLE_VERSION)'/g' \
+	     $< >$@
diff --git a/centos_ci.sh b/centos_ci.sh
index 316897b4d..e5682be2f 100644
--- a/centos_ci.sh
+++ b/centos_ci.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # bundle location
-BUNDLE_VERSION=4.7.1
+BUNDLE_VERSION=4.7.2
 BUNDLE=crc_libvirt_$BUNDLE_VERSION.crcbundle
 GO_VERSION=1.14.13
 
diff --git a/cmd/crc/cmd/daemon.go b/cmd/crc/cmd/daemon.go
index b7b15dbcb..a5e7bea32 100644
--- a/cmd/crc/cmd/daemon.go
+++ b/cmd/crc/cmd/daemon.go
@@ -14,6 +14,7 @@ import (
 	cmdConfig "github.com/code-ready/crc/cmd/crc/cmd/config"
 	"github.com/code-ready/crc/pkg/crc/api"
 	"github.com/code-ready/crc/pkg/crc/constants"
+	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/gvisor-tap-vsock/pkg/types"
 	"github.com/code-ready/gvisor-tap-vsock/pkg/virtualnetwork"
 	"github.com/docker/go-units"
@@ -162,7 +163,7 @@ func run(configuration *types.Configuration) error {
 func runDaemon() error {
 	// Remove if an old socket is present
 	os.Remove(constants.DaemonSocketPath)
-	apiServer, err := api.CreateServer(constants.DaemonSocketPath, config, newMachine())
+	apiServer, err := api.CreateServer(constants.DaemonSocketPath, config, newMachine(), logging.Memory)
 	if err != nil {
 		return err
 	}
diff --git a/docs/source/topics/proc_accessing-the-openshift-cluster-with-oc.adoc b/docs/source/topics/proc_accessing-the-openshift-cluster-with-oc.adoc
index 2d08ad406..67a3b4334 100644
--- a/docs/source/topics/proc_accessing-the-openshift-cluster-with-oc.adoc
+++ b/docs/source/topics/proc_accessing-the-openshift-cluster-with-oc.adoc
@@ -33,11 +33,15 @@ You can also view it by running the [command]`{bin} console --credentials` comma
 ====
 
 . You can now use [command]`oc` to interact with your OpenShift cluster.
-For example, to verify that the OpenShift cluster Operators are available, run the following command with administrator privileges:
+For example, to verify that the OpenShift cluster Operators are available, you need to be logged in as `kubeadmin` user and
+run the following command:
 +
 [subs="+quotes,attributes",options="nowrap"]
 ----
-$ oc get co --as system:admin
+$ oc config use-context crc-admin
+$ oc whoami
+kubeadmin
+$ oc get co
 ----
 +
 [NOTE]
diff --git a/go.mod b/go.mod
index 26cbd0f61..7b12e73c0 100644
--- a/go.mod
+++ b/go.mod
@@ -51,7 +51,7 @@ require (
 	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
 	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
 	github.com/zalando/go-keyring v0.1.1
-	golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
+	golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
 	golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
 	golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19 // indirect
 	golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
diff --git a/go.sum b/go.sum
index bd13dd7b2..534a6fef9 100644
--- a/go.sum
+++ b/go.sum
@@ -966,8 +966,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
-golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
diff --git a/images/rpmbuild/.gitignore b/images/rpmbuild/.gitignore
new file mode 100644
index 000000000..bbcd7072f
--- /dev/null
+++ b/images/rpmbuild/.gitignore
@@ -0,0 +1,1 @@
+Containerfile
diff --git a/images/rpmbuild/Containerfile.in b/images/rpmbuild/Containerfile.in
new file mode 100644
index 000000000..8aecb0d39
--- /dev/null
+++ b/images/rpmbuild/Containerfile.in
@@ -0,0 +1,9 @@
+FROM registry.centos.org/centos
+WORKDIR $APP_ROOT/src
+RUN yum -y install git-core rpm-build dnf-plugins-core 'dnf-command(builddep)'
+COPY . .
+RUN mkdir -p ~/rpmbuild/SOURCES/ && \
+    git archive --format=tar --prefix=crc-__VERSION__-__OPENSHIFT_VERSION__/ HEAD | gzip >~/rpmbuild/SOURCES/crc-__VERSION__.tar.gz
+RUN yum config-manager --set-enabled powertools && \
+    yum -y builddep packaging/rpm/crc.spec && \
+    rpmbuild -bb -v packaging/rpm/crc.spec
diff --git a/packaging/darwin/Distribution.in b/packaging/darwin/Distribution.in
index 905ecfbda..26861de12 100755
--- a/packaging/darwin/Distribution.in
+++ b/packaging/darwin/Distribution.in
@@ -10,9 +10,9 @@
     <installation-check script="installCheck();"/>
     <script>
 function installCheck() {
-    if(!(system.compareVersions(system.version.ProductVersion, '10.12.0') >= 0)) {
+    if(!(system.compareVersions(system.version.ProductVersion, '10.14.0') >= 0)) {
         my.result.title = 'Unable to install';
-        my.result.message = 'CodeReady Containers requires macOS 10.12 or later.';
+        my.result.message = 'CodeReady Containers requires macOS 10.14 or later.';
         my.result.type = 'Fatal';
         return false;
     }
diff --git a/packaging/rpm/.gitignore b/packaging/rpm/.gitignore
new file mode 100644
index 000000000..c0f116255
--- /dev/null
+++ b/packaging/rpm/.gitignore
@@ -0,0 +1,1 @@
+crc.spec
diff --git a/packaging/rpm/crc.spec.in b/packaging/rpm/crc.spec.in
new file mode 100644
index 000000000..3f07156db
--- /dev/null
+++ b/packaging/rpm/crc.spec.in
@@ -0,0 +1,90 @@
+# https://github.com/code-ready/admin-helper
+%global goipath         github.com/code-ready/crc
+Version:                __VERSION__
+%global 	 	openshift_suffix -__OPENSHIFT_VERSION__
+
+%gometa
+
+# debuginfo is not supported on RHEL with Go packages
+%global debug_package %{nil}
+%global _enable_debug_package 0
+
+%global common_description %{expand:
+CodeReady Container's executable}
+
+
+%global golicenses    LICENSE
+%global godocs        *.md
+
+
+Name:           %{goname}
+Release:        1%{?dist}
+Summary:        CodeReady Container's executable
+License:        APL 2.0
+ExcludeArch:    armv7hl i686
+URL:            %{gourl}
+ExcludeArch:    s390x
+Source0:        %{gosource}
+
+#generate_buildrequires
+#go_generate_buildrequires
+
+BuildRequires: git-core
+BuildRequires: go-srpm-macros
+BuildRequires: make
+
+__BUNDLED_REQUIRES__
+
+%description
+%{common_description}
+
+%gopkg
+
+%prep
+# order of these 3 steps is important, build breaks if they are moved around
+%global archivename crc-%{version}%{?openshift_suffix}
+%autosetup -S git -n crc-%{version}%{?openshift_suffix}
+# with fedora macros: goprep -e -k
+install -m 0755 -vd "$(dirname %{gobuilddir}/src/%{goipath})"
+ln -fs "$(pwd)" "%{gobuilddir}/src/%{goipath}"
+
+
+%build
+export GOFLAGS="-mod=vendor"
+make GO_EXTRA_LDFLAGS="-B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \n')" GO_EXTRA_BUILDFLAGS="" cross
+
+%install
+# with fedora macros: gopkginstall
+install -m 0755 -vd                     %{buildroot}%{_bindir}
+install -m 0755 -vp %{gobuilddir}/src/%{goipath}/out/linux-amd64/crc %{buildroot}%{_bindir}/
+
+install -d %{buildroot}%{_datadir}/%{name}-redistributable/{linux,macos,windows}
+install -m 0755 -vp %{gobuilddir}/src/%{goipath}/out/linux-amd64/crc %{buildroot}%{_datadir}/%{name}-redistributable/linux/
+install -m 0755 -vp %{gobuilddir}/src/%{goipath}/out/windows-amd64/crc.exe %{buildroot}%{_datadir}/%{name}-redistributable/windows/
+install -m 0755 -vp %{gobuilddir}/src/%{goipath}/out/macos-amd64/crc %{buildroot}%{_datadir}/%{name}-redistributable/macos/
+
+%check
+# with fedora macros: gocheck
+export GOFLAGS="-mod=vendor"
+# crc uses `go test -race`, which triggers gvisor issues on ppc64le:
+# vendor/gvisor.dev/gvisor/pkg/sync/race_unsafe.go:47:6: missing function body
+# and a ppc64le implementation is indeed missing on ppc64le:
+# https://github.com/google/gvisor/blob/master/pkg/sync/race_unsafe.go
+# https://github.com/google/gvisor/tree/master/pkg/sync
+%ifnarch ppc64le
+make test
+%endif
+
+%files
+%license %{golicenses}
+%doc
+%{_bindir}/*
+%{_datadir}/%{name}-redistributable/linux/*
+%{_datadir}/%{name}-redistributable/macos/*
+%{_datadir}/%{name}-redistributable/windows/*
+
+#gopkgfiles
+
+%changelog
+* Mon Feb 15 2021 Christophe Fergeau <cfergeau@redhat.com> - 1.22.0-1
+- Initial import in Fedora
diff --git a/pkg/crc/api/adapter.go b/pkg/crc/api/adapter.go
index 00e50cd3c..1334d592c 100644
--- a/pkg/crc/api/adapter.go
+++ b/pkg/crc/api/adapter.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/crc/pkg/crc/machine"
+	"github.com/code-ready/machine/libmachine/state"
 )
 
 type AdaptedClient interface {
@@ -126,13 +127,19 @@ func (a *Adapter) Status() ClusterStatusResult {
 }
 
 func (a *Adapter) Stop() Result {
-	_, err := a.Underlying.Stop()
+	vmState, err := a.Underlying.Stop()
 	if err != nil {
 		logging.Error(err)
-		return Result{
-			Name:    a.Underlying.GetName(),
-			Success: false,
-			Error:   err.Error(),
+		if vmState == state.Running {
+			err := a.Underlying.PowerOff()
+			if err != nil {
+				logging.Error(err)
+				return Result{
+					Name:    a.Underlying.GetName(),
+					Success: false,
+					Error:   err.Error(),
+				}
+			}
 		}
 	}
 	return Result{
diff --git a/pkg/crc/api/api.go b/pkg/crc/api/api.go
index 220206e08..7b7953b37 100644
--- a/pkg/crc/api/api.go
+++ b/pkg/crc/api/api.go
@@ -11,22 +11,23 @@ import (
 	"github.com/code-ready/crc/pkg/crc/machine"
 )
 
-func CreateServer(socketPath string, config crcConfig.Storage, machine machine.Client) (Server, error) {
+func CreateServer(socketPath string, config crcConfig.Storage, machine machine.Client, logger Logger) (Server, error) {
 	listener, err := net.Listen("unix", socketPath)
 	if err != nil {
 		logging.Error("Failed to create socket: ", err.Error())
 		return Server{}, err
 	}
-	return createServerWithListener(listener, config, machine)
+	return createServerWithListener(listener, config, machine, logger)
 }
 
-func createServerWithListener(listener net.Listener, config crcConfig.Storage, machine machine.Client) (Server, error) {
+func createServerWithListener(listener net.Listener, config crcConfig.Storage, machine machine.Client, logger Logger) (Server, error) {
 	apiServer := Server{
 		listener:               listener,
 		clusterOpsRequestsChan: make(chan clusterOpsRequest, 10),
 		handler: &Handler{
 			Config:        config,
 			MachineClient: &Adapter{Underlying: machine},
+			Logger:        logger,
 		},
 	}
 	return apiServer, nil
@@ -76,6 +77,8 @@ func (api Server) handleRequest(req commandRequest, conn net.Conn) {
 		result = api.handler.GetConfig(req.Args)
 	case "webconsoleurl":
 		result = api.handler.GetWebconsoleInfo()
+	case "logs":
+		result = api.handler.Logs()
 	default:
 		result = encodeErrorToJSON(fmt.Sprintf("Unknown command supplied: %s", req.Command))
 	}
@@ -114,7 +117,7 @@ func (api Server) handleConnections(conn net.Conn) {
 			conn.Close()
 		}
 
-	case "status", "version", "setconfig", "getconfig", "unsetconfig", "webconsoleurl":
+	case "status", "version", "setconfig", "getconfig", "unsetconfig", "webconsoleurl", "logs":
 		go api.handleRequest(req, conn)
 
 	default:
diff --git a/pkg/crc/api/handlers.go b/pkg/crc/api/handlers.go
index 8684e905f..5eb57df20 100644
--- a/pkg/crc/api/handlers.go
+++ b/pkg/crc/api/handlers.go
@@ -17,10 +17,22 @@ import (
 )
 
 type Handler struct {
+	Logger        Logger
 	MachineClient AdaptedClient
 	Config        crcConfig.Storage
 }
 
+type Logger interface {
+	Messages() []string
+}
+
+func (h *Handler) Logs() string {
+	return encodeStructToJSON(&loggerResult{
+		Success:  true,
+		Messages: h.Logger.Messages(),
+	})
+}
+
 func (h *Handler) Status() string {
 	clusterStatus := h.MachineClient.Status()
 	return encodeStructToJSON(clusterStatus)
diff --git a/pkg/crc/api/types.go b/pkg/crc/api/types.go
index f68e13b83..4909dfc29 100644
--- a/pkg/crc/api/types.go
+++ b/pkg/crc/api/types.go
@@ -21,6 +21,7 @@ type RequestHandler interface {
 	UnsetConfig(json.RawMessage) string
 	GetConfig(json.RawMessage) string
 	GetWebconsoleInfo() string
+	Logs() string
 }
 
 // clusterOpsRequest struct is used to store the command request and associated socket
@@ -52,3 +53,8 @@ type getConfigResult struct {
 type startArgs struct {
 	PullSecretFile string `json:"pullSecretFile"`
 }
+
+type loggerResult struct {
+	Success  bool
+	Messages []string
+}
diff --git a/pkg/crc/cluster/kubeadminPassword.go b/pkg/crc/cluster/kubeadminPassword.go
new file mode 100644
index 000000000..7e2ae1052
--- /dev/null
+++ b/pkg/crc/cluster/kubeadminPassword.go
@@ -0,0 +1,94 @@
+package cluster
+
+import (
+	"crypto/rand"
+	"encoding/base64"
+	"fmt"
+	"math/big"
+
+	"github.com/code-ready/crc/pkg/crc/machine/bundle"
+	"github.com/code-ready/crc/pkg/crc/oc"
+	"golang.org/x/crypto/bcrypt"
+)
+
+// UpdateKubeAdminUserPassword does following
+// - Create and put updated kubeadmin password to ~/.crc/machine/crc/kubeadmin-password
+// - Update the htpasswd secret
+func UpdateKubeAdminUserPassword(kubeAdminPassword string, ocConfig oc.Config, bundle *bundle.CrcBundleInfo) error {
+	// In case of cluster started from stopped state.
+	if kubeAdminPassword == "" {
+		return nil
+	}
+	hashDeveloperPasswd, err := hashBcrypt("developer")
+	if err != nil {
+		return err
+	}
+
+	hashKubeAdminPasswd, err := hashBcrypt(kubeAdminPassword)
+	if err != nil {
+		return err
+	}
+	base64Data := getBase64(hashDeveloperPasswd, hashKubeAdminPasswd)
+
+	cmdArgs := []string{"patch", "secret", "htpass-secret", "-p",
+		fmt.Sprintf(`'{"data":{"htpasswd":"%s"}}'`, base64Data),
+		"-n", "openshift-config", "--type", "merge"}
+	_, stderr, err := ocConfig.RunOcCommandPrivate(cmdArgs...)
+	if err != nil {
+		return fmt.Errorf("Failed to update kubeadmin password %v: %s", err, stderr)
+	}
+	if err := bundle.UpdateKubeadminPassword(kubeAdminPassword); err != nil {
+		return err
+	}
+	return nil
+}
+
+// generateRandomPasswordHash generates a hash of a random ASCII password
+// 5char-5char-5char-5char
+// Copied from openshift/installer https://github.com/openshift/installer/blob/master/pkg/asset/password/password.go
+func GenerateRandomPasswordHash(length int) (string, error) {
+	const (
+		lowerLetters = "abcdefghijkmnopqrstuvwxyz"
+		upperLetters = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
+		digits       = "23456789"
+		all          = lowerLetters + upperLetters + digits
+	)
+	var password string
+	for i := 0; i < length; i++ {
+		n, err := rand.Int(rand.Reader, big.NewInt(int64(len(all))))
+		if err != nil {
+			return "", err
+		}
+		newchar := string(all[n.Int64()])
+		if password == "" {
+			password = newchar
+		}
+		if i < length-1 {
+			n, err = rand.Int(rand.Reader, big.NewInt(int64(len(password)+1)))
+			if err != nil {
+				return "", err
+			}
+			j := n.Int64()
+			password = password[0:j] + newchar + password[j:]
+		}
+	}
+	pw := []rune(password)
+	for _, replace := range []int{5, 11, 17} {
+		pw[replace] = '-'
+	}
+
+	return string(pw), nil
+}
+
+func hashBcrypt(password string) (hash string, err error) {
+	passwordBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+	if err != nil {
+		return
+	}
+	return string(passwordBytes), nil
+}
+
+func getBase64(developerUserPassword, adminUserPassword string) string {
+	s := fmt.Sprintf("developer:%s\nkubeadmin:%s", developerUserPassword, adminUserPassword)
+	return base64.StdEncoding.EncodeToString([]byte(s))
+}
diff --git a/pkg/crc/logging/inmemory.go b/pkg/crc/logging/inmemory.go
new file mode 100644
index 000000000..756014739
--- /dev/null
+++ b/pkg/crc/logging/inmemory.go
@@ -0,0 +1,38 @@
+package logging
+
+import (
+	"container/ring"
+
+	"github.com/sirupsen/logrus"
+)
+
+// This hook keeps in memory n messages from error to info level
+type inMemoryHook struct {
+	messages *ring.Ring
+}
+
+func newInMemoryHook(size int) *inMemoryHook {
+	return &inMemoryHook{
+		messages: ring.New(size),
+	}
+}
+
+func (h *inMemoryHook) Levels() []logrus.Level {
+	return []logrus.Level{logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel}
+}
+
+func (h *inMemoryHook) Fire(entry *logrus.Entry) error {
+	h.messages.Value = entry.Message
+	h.messages = h.messages.Next()
+	return nil
+}
+
+func (h *inMemoryHook) Messages() []string {
+	var ret []string
+	h.messages.Do(func(elem interface{}) {
+		if str, ok := elem.(string); ok {
+			ret = append(ret, str)
+		}
+	})
+	return ret
+}
diff --git a/pkg/crc/logging/logging.go b/pkg/crc/logging/logging.go
index 370730795..8645dc33d 100644
--- a/pkg/crc/logging/logging.go
+++ b/pkg/crc/logging/logging.go
@@ -14,6 +14,7 @@ var (
 	logfile       *os.File
 	LogLevel      string
 	originalHooks = logrus.LevelHooks{}
+	Memory        = newInMemoryHook(100)
 )
 
 func OpenLogFile(path string) (*os.File, error) {
@@ -53,6 +54,8 @@ func InitLogrus(logLevel, logFilePath string) {
 		level = logrus.InfoLevel
 	}
 
+	logrus.AddHook(Memory)
+
 	// Add hook to send error/fatal to stderr
 	logrus.AddHook(newstdErrHook(level, &logrus.TextFormatter{
 		ForceColors:            terminal.IsTerminal(int(os.Stderr.Fd())),
diff --git a/pkg/crc/machine/bundle/metadata.go b/pkg/crc/machine/bundle/metadata.go
index d55efab0c..e4f874178 100644
--- a/pkg/crc/machine/bundle/metadata.go
+++ b/pkg/crc/machine/bundle/metadata.go
@@ -111,13 +111,27 @@ func (bundle *CrcBundleInfo) GetInitramfsPath() string {
 }
 
 func (bundle *CrcBundleInfo) GetKubeadminPassword() (string, error) {
-	rawData, err := ioutil.ReadFile(bundle.resolvePath(bundle.ClusterInfo.KubeadminPasswordFile))
+	// First check if ~/.crc/machine/crc/kubeadmin-password file exist and read password from there
+	kubeAdminPasswordFile := filepath.Join(constants.MachineInstanceDir, constants.DefaultName, bundle.ClusterInfo.KubeadminPasswordFile)
+	if _, err := os.Stat(kubeAdminPasswordFile); err != nil {
+		kubeAdminPasswordFile = bundle.resolvePath(bundle.ClusterInfo.KubeadminPasswordFile)
+	}
+	rawData, err := ioutil.ReadFile(kubeAdminPasswordFile)
 	if err != nil {
 		return "", err
 	}
 	return strings.TrimSpace(string(rawData)), nil
 }
 
+func (bundle *CrcBundleInfo) UpdateKubeadminPassword(kubeadminPassword string) error {
+	kubeAdminPasswordFile := filepath.Join(constants.MachineInstanceDir, constants.DefaultName, bundle.ClusterInfo.KubeadminPasswordFile)
+	err := ioutil.WriteFile(kubeAdminPasswordFile, []byte(kubeadminPassword), 0600)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
 func (bundle *CrcBundleInfo) GetBundleBuildTime() (time.Time, error) {
 	return time.Parse(time.RFC3339, strings.TrimSpace(bundle.BuildInfo.BuildTime))
 }
diff --git a/pkg/crc/machine/console.go b/pkg/crc/machine/console.go
index 630496204..c5c1a5ff0 100644
--- a/pkg/crc/machine/console.go
+++ b/pkg/crc/machine/console.go
@@ -24,7 +24,7 @@ func (client *client) GetConsoleURL() (*ConsoleResult, error) {
 		return nil, errors.Wrap(err, "Error loading bundle metadata")
 	}
 
-	clusterConfig, err := getClusterConfig(crcBundleMetadata)
+	clusterConfig, err := getClusterConfig(crcBundleMetadata, "")
 	if err != nil {
 		return nil, errors.Wrap(err, "Error loading cluster configuration")
 	}
diff --git a/pkg/crc/machine/machine.go b/pkg/crc/machine/machine.go
index ea0e12050..ae7fdd3df 100644
--- a/pkg/crc/machine/machine.go
+++ b/pkg/crc/machine/machine.go
@@ -12,11 +12,14 @@ import (
 	"github.com/code-ready/machine/libmachine/drivers"
 )
 
-func getClusterConfig(bundleInfo *bundle.CrcBundleInfo) (*ClusterConfig, error) {
+func getClusterConfig(bundleInfo *bundle.CrcBundleInfo, generatedKubeadminPassword string) (*ClusterConfig, error) {
 	kubeadminPassword, err := bundleInfo.GetKubeadminPassword()
 	if err != nil {
 		return nil, fmt.Errorf("Error reading kubeadmin password from bundle %v", err)
 	}
+	if generatedKubeadminPassword != "" {
+		kubeadminPassword = generatedKubeadminPassword
+	}
 	proxyConfig, err := getProxyConfig(bundleInfo.ClusterInfo.BaseDomain)
 	if err != nil {
 		return nil, err
diff --git a/pkg/crc/machine/start.go b/pkg/crc/machine/start.go
index 61fd5f031..84e54b0bc 100644
--- a/pkg/crc/machine/start.go
+++ b/pkg/crc/machine/start.go
@@ -108,6 +108,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 
 	// Pre-VM start
 	var host *host.Host
+	var kubeAdminUserPassword string
 	exists, err := client.Exists()
 	if err != nil {
 		return nil, errors.Wrap(err, "Cannot determine if VM exists")
@@ -131,6 +132,11 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 			NetworkMode: client.networkMode(),
 		}
 
+		kubeAdminUserPassword, err = cluster.GenerateRandomPasswordHash(23)
+		if err != nil {
+			return nil, errors.Wrap(err, "Cannot generate the kubeadmin user password")
+		}
+
 		crcBundleMetadata, err = getCrcBundleInfo(startConfig.BundlePath)
 		if err != nil {
 			return nil, errors.Wrap(err, "Error getting bundle metadata")
@@ -180,7 +186,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 		}
 		if vmState == state.Running {
 			logging.Infof("A CodeReady Containers VM for OpenShift %s is already running", crcBundleMetadata.GetOpenshiftVersion())
-			clusterConfig, err := getClusterConfig(crcBundleMetadata)
+			clusterConfig, err := getClusterConfig(crcBundleMetadata, kubeAdminUserPassword)
 			if err != nil {
 				return nil, errors.Wrap(err, "Cannot create cluster configuration")
 			}
@@ -222,7 +228,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 		}
 	}
 
-	clusterConfig, err := getClusterConfig(crcBundleMetadata)
+	clusterConfig, err := getClusterConfig(crcBundleMetadata, kubeAdminUserPassword)
 	if err != nil {
 		return nil, errors.Wrap(err, "Cannot create cluster configuration")
 	}
@@ -366,6 +372,10 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
 		return nil, errors.Wrap(err, "Failed to update cluster pull secret")
 	}
 
+	if err := cluster.UpdateKubeAdminUserPassword(kubeAdminUserPassword, ocConfig, crcBundleMetadata); err != nil {
+		return nil, errors.Wrap(err, "Failed to update kubeadmin user password")
+	}
+
 	if err := cluster.EnsureClusterIDIsNotEmpty(ocConfig); err != nil {
 		return nil, errors.Wrap(err, "Failed to update cluster ID")
 	}
diff --git a/vendor/golang.org/x/crypto/bcrypt/base64.go b/vendor/golang.org/x/crypto/bcrypt/base64.go
new file mode 100644
index 000000000..fc3116090
--- /dev/null
+++ b/vendor/golang.org/x/crypto/bcrypt/base64.go
@@ -0,0 +1,35 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bcrypt
+
+import "encoding/base64"
+
+const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+
+var bcEncoding = base64.NewEncoding(alphabet)
+
+func base64Encode(src []byte) []byte {
+	n := bcEncoding.EncodedLen(len(src))
+	dst := make([]byte, n)
+	bcEncoding.Encode(dst, src)
+	for dst[n-1] == '=' {
+		n--
+	}
+	return dst[:n]
+}
+
+func base64Decode(src []byte) ([]byte, error) {
+	numOfEquals := 4 - (len(src) % 4)
+	for i := 0; i < numOfEquals; i++ {
+		src = append(src, '=')
+	}
+
+	dst := make([]byte, bcEncoding.DecodedLen(len(src)))
+	n, err := bcEncoding.Decode(dst, src)
+	if err != nil {
+		return nil, err
+	}
+	return dst[:n], nil
+}
diff --git a/vendor/golang.org/x/crypto/bcrypt/bcrypt.go b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
new file mode 100644
index 000000000..aeb73f81a
--- /dev/null
+++ b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
@@ -0,0 +1,295 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
+// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
+package bcrypt // import "golang.org/x/crypto/bcrypt"
+
+// The code is a port of Provos and Mazières's C implementation.
+import (
+	"crypto/rand"
+	"crypto/subtle"
+	"errors"
+	"fmt"
+	"io"
+	"strconv"
+
+	"golang.org/x/crypto/blowfish"
+)
+
+const (
+	MinCost     int = 4  // the minimum allowable cost as passed in to GenerateFromPassword
+	MaxCost     int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
+	DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
+)
+
+// The error returned from CompareHashAndPassword when a password and hash do
+// not match.
+var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
+
+// The error returned from CompareHashAndPassword when a hash is too short to
+// be a bcrypt hash.
+var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
+
+// The error returned from CompareHashAndPassword when a hash was created with
+// a bcrypt algorithm newer than this implementation.
+type HashVersionTooNewError byte
+
+func (hv HashVersionTooNewError) Error() string {
+	return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
+}
+
+// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
+type InvalidHashPrefixError byte
+
+func (ih InvalidHashPrefixError) Error() string {
+	return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
+}
+
+type InvalidCostError int
+
+func (ic InvalidCostError) Error() string {
+	return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
+}
+
+const (
+	majorVersion       = '2'
+	minorVersion       = 'a'
+	maxSaltSize        = 16
+	maxCryptedHashSize = 23
+	encodedSaltSize    = 22
+	encodedHashSize    = 31
+	minHashSize        = 59
+)
+
+// magicCipherData is an IV for the 64 Blowfish encryption calls in
+// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
+var magicCipherData = []byte{
+	0x4f, 0x72, 0x70, 0x68,
+	0x65, 0x61, 0x6e, 0x42,
+	0x65, 0x68, 0x6f, 0x6c,
+	0x64, 0x65, 0x72, 0x53,
+	0x63, 0x72, 0x79, 0x44,
+	0x6f, 0x75, 0x62, 0x74,
+}
+
+type hashed struct {
+	hash  []byte
+	salt  []byte
+	cost  int // allowed range is MinCost to MaxCost
+	major byte
+	minor byte
+}
+
+// GenerateFromPassword returns the bcrypt hash of the password at the given
+// cost. If the cost given is less than MinCost, the cost will be set to
+// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
+// to compare the returned hashed password with its cleartext version.
+func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
+	p, err := newFromPassword(password, cost)
+	if err != nil {
+		return nil, err
+	}
+	return p.Hash(), nil
+}
+
+// CompareHashAndPassword compares a bcrypt hashed password with its possible
+// plaintext equivalent. Returns nil on success, or an error on failure.
+func CompareHashAndPassword(hashedPassword, password []byte) error {
+	p, err := newFromHash(hashedPassword)
+	if err != nil {
+		return err
+	}
+
+	otherHash, err := bcrypt(password, p.cost, p.salt)
+	if err != nil {
+		return err
+	}
+
+	otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
+	if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
+		return nil
+	}
+
+	return ErrMismatchedHashAndPassword
+}
+
+// Cost returns the hashing cost used to create the given hashed
+// password. When, in the future, the hashing cost of a password system needs
+// to be increased in order to adjust for greater computational power, this
+// function allows one to establish which passwords need to be updated.
+func Cost(hashedPassword []byte) (int, error) {
+	p, err := newFromHash(hashedPassword)
+	if err != nil {
+		return 0, err
+	}
+	return p.cost, nil
+}
+
+func newFromPassword(password []byte, cost int) (*hashed, error) {
+	if cost < MinCost {
+		cost = DefaultCost
+	}
+	p := new(hashed)
+	p.major = majorVersion
+	p.minor = minorVersion
+
+	err := checkCost(cost)
+	if err != nil {
+		return nil, err
+	}
+	p.cost = cost
+
+	unencodedSalt := make([]byte, maxSaltSize)
+	_, err = io.ReadFull(rand.Reader, unencodedSalt)
+	if err != nil {
+		return nil, err
+	}
+
+	p.salt = base64Encode(unencodedSalt)
+	hash, err := bcrypt(password, p.cost, p.salt)
+	if err != nil {
+		return nil, err
+	}
+	p.hash = hash
+	return p, err
+}
+
+func newFromHash(hashedSecret []byte) (*hashed, error) {
+	if len(hashedSecret) < minHashSize {
+		return nil, ErrHashTooShort
+	}
+	p := new(hashed)
+	n, err := p.decodeVersion(hashedSecret)
+	if err != nil {
+		return nil, err
+	}
+	hashedSecret = hashedSecret[n:]
+	n, err = p.decodeCost(hashedSecret)
+	if err != nil {
+		return nil, err
+	}
+	hashedSecret = hashedSecret[n:]
+
+	// The "+2" is here because we'll have to append at most 2 '=' to the salt
+	// when base64 decoding it in expensiveBlowfishSetup().
+	p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
+	copy(p.salt, hashedSecret[:encodedSaltSize])
+
+	hashedSecret = hashedSecret[encodedSaltSize:]
+	p.hash = make([]byte, len(hashedSecret))
+	copy(p.hash, hashedSecret)
+
+	return p, nil
+}
+
+func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
+	cipherData := make([]byte, len(magicCipherData))
+	copy(cipherData, magicCipherData)
+
+	c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
+	if err != nil {
+		return nil, err
+	}
+
+	for i := 0; i < 24; i += 8 {
+		for j := 0; j < 64; j++ {
+			c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
+		}
+	}
+
+	// Bug compatibility with C bcrypt implementations. We only encode 23 of
+	// the 24 bytes encrypted.
+	hsh := base64Encode(cipherData[:maxCryptedHashSize])
+	return hsh, nil
+}
+
+func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
+	csalt, err := base64Decode(salt)
+	if err != nil {
+		return nil, err
+	}
+
+	// Bug compatibility with C bcrypt implementations. They use the trailing
+	// NULL in the key string during expansion.
+	// We copy the key to prevent changing the underlying array.
+	ckey := append(key[:len(key):len(key)], 0)
+
+	c, err := blowfish.NewSaltedCipher(ckey, csalt)
+	if err != nil {
+		return nil, err
+	}
+
+	var i, rounds uint64
+	rounds = 1 << cost
+	for i = 0; i < rounds; i++ {
+		blowfish.ExpandKey(ckey, c)
+		blowfish.ExpandKey(csalt, c)
+	}
+
+	return c, nil
+}
+
+func (p *hashed) Hash() []byte {
+	arr := make([]byte, 60)
+	arr[0] = '$'
+	arr[1] = p.major
+	n := 2
+	if p.minor != 0 {
+		arr[2] = p.minor
+		n = 3
+	}
+	arr[n] = '$'
+	n++
+	copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
+	n += 2
+	arr[n] = '$'
+	n++
+	copy(arr[n:], p.salt)
+	n += encodedSaltSize
+	copy(arr[n:], p.hash)
+	n += encodedHashSize
+	return arr[:n]
+}
+
+func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
+	if sbytes[0] != '$' {
+		return -1, InvalidHashPrefixError(sbytes[0])
+	}
+	if sbytes[1] > majorVersion {
+		return -1, HashVersionTooNewError(sbytes[1])
+	}
+	p.major = sbytes[1]
+	n := 3
+	if sbytes[2] != '$' {
+		p.minor = sbytes[2]
+		n++
+	}
+	return n, nil
+}
+
+// sbytes should begin where decodeVersion left off.
+func (p *hashed) decodeCost(sbytes []byte) (int, error) {
+	cost, err := strconv.Atoi(string(sbytes[0:2]))
+	if err != nil {
+		return -1, err
+	}
+	err = checkCost(cost)
+	if err != nil {
+		return -1, err
+	}
+	p.cost = cost
+	return 3, nil
+}
+
+func (p *hashed) String() string {
+	return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
+}
+
+func checkCost(cost int) error {
+	if cost < MinCost || cost > MaxCost {
+		return InvalidCostError(cost)
+	}
+	return nil
+}
diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go
index c474e5a80..94c71ac1a 100644
--- a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go
+++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.11 && gc && !purego
 // +build go1.11,gc,!purego
 
 package chacha20
diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go
index 3e8a609fb..025b49897 100644
--- a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go
+++ b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego
 // +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
 
 package chacha20
diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go
index 2806c6325..da420b2e9 100644
--- a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go
+++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gc && !purego
 // +build gc,!purego
 
 package chacha20
diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go
index a0774dde1..c5898db46 100644
--- a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go
+++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gc && !purego
 // +build gc,!purego
 
 package chacha20
diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go
index 877b6de29..84858480d 100644
--- a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go
+++ b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build amd64 && gc && !purego
 // +build amd64,gc,!purego
 
 package curve25519
diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go b/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go
index 80d3300af..259728af7 100644
--- a/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go
+++ b/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !amd64 || !gc || purego
 // +build !amd64 !gc purego
 
 package curve25519
diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go
index c7f8c7e64..71ad917da 100644
--- a/vendor/golang.org/x/crypto/ed25519/ed25519.go
+++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go
@@ -5,6 +5,7 @@
 // In Go 1.13, the ed25519 package was promoted to the standard library as
 // crypto/ed25519, and this package became a wrapper for the standard library one.
 //
+//go:build !go1.13
 // +build !go1.13
 
 // Package ed25519 implements the Ed25519 signature algorithm. See
diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go
index d1448d8d2..b5974dc8b 100644
--- a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go
+++ b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.13
 // +build go1.13
 
 // Package ed25519 implements the Ed25519 signature algorithm. See
diff --git a/vendor/golang.org/x/crypto/internal/subtle/aliasing.go b/vendor/golang.org/x/crypto/internal/subtle/aliasing.go
index 281c27ef0..4fad24f8d 100644
--- a/vendor/golang.org/x/crypto/internal/subtle/aliasing.go
+++ b/vendor/golang.org/x/crypto/internal/subtle/aliasing.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !purego
 // +build !purego
 
 // Package subtle implements functions that are often useful in cryptographic
diff --git a/vendor/golang.org/x/crypto/internal/subtle/aliasing_purego.go b/vendor/golang.org/x/crypto/internal/subtle/aliasing_purego.go
index e20a29659..80ccbed2c 100644
--- a/vendor/golang.org/x/crypto/internal/subtle/aliasing_purego.go
+++ b/vendor/golang.org/x/crypto/internal/subtle/aliasing_purego.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build purego
 // +build purego
 
 // Package subtle implements functions that are often useful in cryptographic
diff --git a/vendor/golang.org/x/crypto/poly1305/bits_compat.go b/vendor/golang.org/x/crypto/poly1305/bits_compat.go
index 157a69f61..45b5c966b 100644
--- a/vendor/golang.org/x/crypto/poly1305/bits_compat.go
+++ b/vendor/golang.org/x/crypto/poly1305/bits_compat.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.13
 // +build !go1.13
 
 package poly1305
diff --git a/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go b/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go
index a0a185f0f..ed52b3418 100644
--- a/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go
+++ b/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.13
 // +build go1.13
 
 package poly1305
diff --git a/vendor/golang.org/x/crypto/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/poly1305/mac_noasm.go
index af6c94f92..f184b67d9 100644
--- a/vendor/golang.org/x/crypto/poly1305/mac_noasm.go
+++ b/vendor/golang.org/x/crypto/poly1305/mac_noasm.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
 // +build !amd64,!ppc64le,!s390x !gc purego
 
 package poly1305
diff --git a/vendor/golang.org/x/crypto/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go
index cf3a69ed3..6d522333f 100644
--- a/vendor/golang.org/x/crypto/poly1305/sum_amd64.go
+++ b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gc && !purego
 // +build gc,!purego
 
 package poly1305
diff --git a/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go
index cb4b7185d..4a069941a 100644
--- a/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go
+++ b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gc && !purego
 // +build gc,!purego
 
 package poly1305
diff --git a/vendor/golang.org/x/crypto/poly1305/sum_s390x.go b/vendor/golang.org/x/crypto/poly1305/sum_s390x.go
index 188a665e1..62cc9f847 100644
--- a/vendor/golang.org/x/crypto/poly1305/sum_s390x.go
+++ b/vendor/golang.org/x/crypto/poly1305/sum_s390x.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build gc && !purego
 // +build gc,!purego
 
 package poly1305
diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go
index 7d42a8c88..b6911e830 100644
--- a/vendor/golang.org/x/crypto/ssh/server.go
+++ b/vendor/golang.org/x/crypto/ssh/server.go
@@ -572,6 +572,10 @@ userAuthLoop:
 				perms = candidate.perms
 			}
 		case "gssapi-with-mic":
+			if config.GSSAPIWithMICConfig == nil {
+				authErr = errors.New("ssh: gssapi-with-mic auth not configured")
+				break
+			}
 			gssapiConfig := config.GSSAPIWithMICConfig
 			userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload)
 			if err != nil {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index aefacc767..1bbab9d68 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -290,8 +290,9 @@ github.com/xtgo/uuid
 ## explicit
 github.com/zalando/go-keyring
 github.com/zalando/go-keyring/secret_service
-# golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
+# golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
 ## explicit
+golang.org/x/crypto/bcrypt
 golang.org/x/crypto/blowfish
 golang.org/x/crypto/chacha20
 golang.org/x/crypto/curve25519


Test patch:
diff --git a/cmd/crc/cmd/start_test.go b/cmd/crc/cmd/start_test.go
index a65ff7a64..38e261bc0 100644
--- a/cmd/crc/cmd/start_test.go
+++ b/cmd/crc/cmd/start_test.go
@@ -34,7 +34,15 @@ func TestRenderActionPlainSuccess(t *testing.T) {
 		},
 	}, out, ""))
 
+	var userShell string
 	userShell, err := shell.GetShell("")
+	if err != nil {
+		if runtime.GOOS == "windows" {
+			userShell, err = shell.GetShell("cmd")
+		} else {
+			userShell, err = shell.GetShell("bash")
+		}
+	}
 	assert.NoError(t, err)
 	assert.Equal(t, expectedTemplate(userShell), out.String())
 }
diff --git a/pkg/crc/api/api_test.go b/pkg/crc/api/api_test.go
index e97f8119a..76822ad87 100644
--- a/pkg/crc/api/api_test.go
+++ b/pkg/crc/api/api_test.go
@@ -31,7 +31,7 @@ func TestApi(t *testing.T) {
 	require.NoError(t, err)
 
 	client := fakemachine.NewClient()
-	api, err := createServerWithListener(listener, setupNewInMemoryConfig(), client)
+	api, err := createServerWithListener(listener, setupNewInMemoryConfig(), client, &mockLogger{})
 	require.NoError(t, err)
 	go func() {
 		if err := api.Serve(); err != nil {
@@ -118,6 +118,13 @@ func TestApi(t *testing.T) {
 				},
 			},
 		},
+		{
+			command: "logs",
+			expected: map[string]interface{}{
+				"Messages": []interface{}{"message 1", "message 2", "message 3"},
+				"Success":  true,
+			},
+		},
 	}
 	for _, test := range tt {
 		client.Failing = test.clientFailing
@@ -218,7 +225,7 @@ func setupAPIServer(t *testing.T) (string, func()) {
 	require.NoError(t, err)
 
 	client := fakemachine.NewClient()
-	api, err := createServerWithListener(listener, setupNewInMemoryConfig(), client)
+	api, err := createServerWithListener(listener, setupNewInMemoryConfig(), client, &mockLogger{})
 	require.NoError(t, err)
 	go func() {
 		if err := api.Serve(); err != nil {
@@ -247,3 +254,10 @@ func (s *skipPreflights) Set(key string, value interface{}) error {
 func (s *skipPreflights) Unset(key string) error {
 	return s.storage.Unset(key)
 }
+
+type mockLogger struct {
+}
+
+func (*mockLogger) Messages() []string {
+	return []string{"message 1", "message 2", "message 3"}
+}
diff --git a/pkg/crc/logging/inmemory_test.go b/pkg/crc/logging/inmemory_test.go
new file mode 100644
index 000000000..3061878aa
--- /dev/null
+++ b/pkg/crc/logging/inmemory_test.go
@@ -0,0 +1,23 @@
+package logging
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/sirupsen/logrus"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAddLogs(t *testing.T) {
+	memory := newInMemoryHook(5)
+
+	assert.Len(t, memory.Messages(), 0)
+
+	for i := 1; i < 10; i++ {
+		assert.NoError(t, memory.Fire(&logrus.Entry{
+			Message: fmt.Sprintf("message %d", i),
+		}))
+	}
+
+	assert.Equal(t, []string{"message 5", "message 6", "message 7", "message 8", "message 9"}, memory.Messages())
+}
diff --git a/pkg/os/util_test.go b/pkg/os/util_test.go
index 5dd42e050..7a69f4845 100644
--- a/pkg/os/util_test.go
+++ b/pkg/os/util_test.go
@@ -7,6 +7,8 @@ import (
 	"runtime"
 	"testing"
 
+	"os/user"
+
 	"github.com/stretchr/testify/assert"
 )
 
@@ -82,7 +84,13 @@ func TestFileExists(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		assert.True(t, FileExists(filename))
 	} else {
-		assert.False(t, FileExists(filename))
+		user, _ := user.Current()
+		if user != nil && user.Uid != "0" {
+			assert.False(t, FileExists(filename))
+		} else {
+			/* If the user is root, chmod 000 $dir won't block file existence checks */
+			assert.True(t, FileExists(filename))
+		}
 	}
 
 	filename = filepath.Join(dirname, "nonexistent")