'Cannot COPY from previous stage in DockerFIle

This looks like a common issue so I checked a few SO posts but none of them solved my problem.

Here is my Dockerfile:

# MkDocs container
FROM python:3-alpine AS build-env

RUN apk add bash

RUN pip install --upgrade pip
RUN pip install pymdown-extensions \
&& pip install mkdocs \
&& pip install mkdocs-material \
&& pip install mkdocs-rtd-dropdown \
&& pip install mkdocs-git-revision-date-plugin \
&& pip install mkdocs-git-revision-date-localized-plugin \
&& pip install mkdocs-redirects

# executed at ~/Developer/MkDocs
RUN mkdir -p /home/mkdocs/
WORKDIR /home/mkdocs/
COPY . .
RUN mkdocs build -s
WORKDIR /

# Nginx container
FROM nginx:1.21.6-alpine
RUN apk add bash
EXPOSE 80

RUN cat /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/nginx.conf

WORKDIR /
RUN mkdir -p /home/mkdocs
COPY --from=build-env /home/mkdocs/site/ /home/mkdocs/
RUN mv /home/mkdocs/* /usr/share/nginx/html/
RUN chown nginx:nginx /usr/share/nginx/html/*

USER nginx:nginx

And here is the command to run docker:

docker run -it --name mkdocs -p 8789:80 nginx

Running localhost:8789 only shows the default nginx homepage, not the built one of MkDocs. I also run docker exec -it --user root <PID> bash to check the directory /usr/share/nginx/html/ but the copied files are not there.

My other checks:

  • First, I'm 100% sure that the files built in the first stage works and exists

  • Second, this is what completely frustrated me out. If I run the docker using docker run -it --entrypoint=/bin/bash mkdocs:v1, I can actually see the built MkDocs files:

bash-5.1$ ls /usr/share/nginx/html
404.html        50x.html        assets          index.html      search          sitemap.xml     sitemap.xml.gz
bash-5.1$


Solution 1:[1]

This isn't a Rust-specific problem, although the vocabulary may be a little different. The ideal way to solve a problem like this, not just with traits in Rust but in any language, is to add the desired behavior (foo_method in your example) to the abstract interface (Trait):

trait Trait {
    fn trait_method(&self);
    fn foo_method(&self) {} // does nothing by default
}

struct Foo;

impl Trait for Foo {
    fn trait_method(&self) {
        println!("In trait_method of Foo");
    }

    fn foo_method(&self) {
        // override default behavior
        println!("In foo_method");
    }
}

struct Bar;

impl Trait for Bar {
    fn trait_method(&self) {
        println!("In trait_method of Bar");
    }
}

fn main() {
    let vec: Vec<Box<dyn Trait>> = vec![Box::new(Foo), Box::new(Bar)];

    for e in &vec {
        e.trait_method();
        e.foo_method();
    }
}

In this example, I have put a default implementation of foo_method in Trait which does nothing, so that you don't have to define it in every impl but only the one(s) where it applies. You should really attempt to make the above work before you resort to downcasting to a concrete type, which has serious drawbacks that all but erase the advantages of having trait objects in the first place.

That said, there are cases where downcasting may be necessary, and Rust does support it -- although the interface is a little clunky. You can downcast &Trait to &Foo by adding an intermediate upcast to &Any:

use std::any::Any;

trait Trait {
    fn as_any(&self) -> &dyn Any;
}

struct Foo;

impl Trait for Foo {
    fn as_any(&self) -> &dyn Any {
        self
    }
}

fn downcast<T: Trait + 'static>(this: &dyn Trait) -> Option<&T> {
    this.as_any().downcast_ref()
}

as_any has to be a method in Trait because it needs access to the concrete type. Now you can attempt to call Foo methods on a Trait trait object like this (complete playground example):

if let Some(r) = downcast::<Foo>(&**e) {
    r.foo_method();
}

To make this work, you have to specify what type you expect (::<Foo>) and use if let to handle what happens when the referenced object is not an instance of Foo. You can't downcast a trait object to a concrete type unless you know exactly what concrete type it is.

If you ever need to know the concrete type, trait objects are almost useless anyway! You probably should use an enum instead, so that you will get compile-time errors if you omit to handle a variant somewhere. Furthermore, you can't use Any with non-'static structs, so if any Foo might need to contain a reference, this design is a dead end. The best solution, if you can do it, is to add foo_method to the trait itself.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Shepmaster