.dockerignore not being read on image build - docker

I've been stuck on this for awhile. I would like docker to ignore a particular directory when building an image, because my user account does not have permissions to read that directory. I cannot move it, so that's not an alternative.
This is the structure of my project. docker/data is the directory that I do not have permissions to read, and docker/node-express.dockerfile is the image I'm trying to build.
Running docker build --no-cache --tag node-express --file ./docker/node-express.dockerfile . in the root directory outputs the error
error checking context: 'can't stat '/home/anthony/Repositories/Anthony-Monterrosa/aws-postgres-node-stack/docker/data''.
After this error and a bit of googling I learned about .dockerignore files, and made one in the root directory. The following is the file's text.
docker/data
I ran the command again but got an identical error. A bit more googling and I found out about image-specific .dockerignore files, so I set DOCKER_BUILDKIT to 1, created docker/node-express.dockerfile.dockerignore with the following content
data
docker/data
(I am not sure how relative paths work with image-specific .dockerignores, so I added both). Ran the command again, but still with the same error.
So, I don't seem to have ignores working correctly with either .dockerignore file, or both. What am I missing here?

The error is:
error checking context: 'can't stat '/home/anthony/Repositories/Anthony-Monterrosa/aws-postgres-node-stack/docker/data''.
So looks there is some operation before .dockerignore effect.
As there is no context content in your docker folder, I suggest you just add docker in .dockerignore.
This way, although still error, but the build will continue like next:
shubuntu1#shubuntu1:~/trial2020/trial$ docker build -t abcd:1 -f docker/Dockerfile .
ERRO[0000] Tar: Can't stat file /home/shubuntu1/trial2020/trial to tar: open
/home/shubuntu1/trial2020/trial/docker/data: permission denied
Sending build context to Docker daemon 3.072kB
Step 1/1 : FROM ubuntu:18.04
---> 3556258649b2
Successfully built 3556258649b2
Successfully tagged abcd:1
UPDATE why according to your comments:
You may want to have a look for docker-ce source code, build.go & context.go:
build.go:
if err := build.ValidateContextDirectory(contextDir, excludes); err != nil {
return errors.Errorf("error checking context: '%s'.", err)
}
context.go:
func ValidateContextDirectory(srcPath string, excludes []string) error {
contextRoot, err := getContextRoot(srcPath)
if err != nil {
return err
}
pm, err := fileutils.NewPatternMatcher(excludes)
if err != nil {
return err
}
return filepath.Walk(contextRoot, func(filePath string, f os.FileInfo, err error) error {
if err != nil {
if os.IsPermission(err) {
return errors.Errorf("can't stat '%s'", filePath)
}
if os.IsNotExist(err) {
return errors.Errorf("file ('%s') not found or excluded by .dockerignore", filePath)
}
return err
}
// skip this directory/file if it's not in the path, it won't get added to the context
if relFilePath, err := filepath.Rel(contextRoot, filePath); err != nil {
return err
} else if skip, err := filepathMatches(pm, relFilePath); err != nil {
return err
} else if skip {
if f.IsDir() {
return filepath.SkipDir
}
return nil
}
......
})
}
Before docker daemon tar the build context, it will first try to validate the context directory:
docker/data in .dockerignore:
It will use Walk to ergodic all things under docker, when it comes to docker/data, next code finally make the build exit, so you did not get image generated:
if os.IsPermission(err) {
return errors.Errorf("can't stat '%s'", filePath)
}
docker in .dockerignore:
Same as above, difference is next code will effect when comes to the match docker in .dockerignore:
return filepath.SkipDir
This will make the Walk ignore the subfolders of docker, then docker/data no chance to be ergodic, so no permission error there.
The ERRO[0000] Tar: Can't stat file comes from other later steps which won't exit the image build.

Related

query a docker registry (hub.docker.com) using go docker client without docker daemon dependency

I'm trying to access a docker registry (public or private) using Go. A simple program which can access any registry and verify if an image is present.
I looked at docker client available in Go https://pkg.go.dev/github.com/docker/docker#v20.10.11+incompatible/client
But the problem is, this client needs a docker daemon running in order to work. Is there any way to query a docker registry (ex: hub.docker.com) without any dependency on underlying docker engine?
My idea is to run this program on a docker container and there wont be any docker engine running inside a container. And I don't want to run docker inside docker or any sort of hack. I just want to connect to a registry and query an image. And please don't quote other questions in stack overflow. No one has answered this.
This is what I have done so far
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/docker/docker/api/types/filters"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func main() {
cli, err := client.NewClientWithOpts(client.WithHost("https://hub.docker.com"), client.WithAPIVersionNegotiation())
if err != nil {
fmt.Println(err.Error())
return
}
err = imagemanifest(cli)
if err != nil {
fmt.Println(err)
}
err = imageSearch(cli)
}
func imagemanifest(dockerClient *client.Client) error {
var authConfig = types.AuthConfig{
Username: "amokkara",
Password: "M#vr1ck2009",
ServerAddress: "https://index.docker.io/v2/",
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*1200)
defer cancel()
authConfigBytes, _ := json.Marshal(authConfig)
authConfigEncoded := base64.URLEncoding.EncodeToString(authConfigBytes)
ctx, cancel = context.WithTimeout(context.Background(), time.Second*1200)
defer cancel()
searchres , err := dockerClient.DistributionInspect(ctx,"amokkara/amokkara:3",authConfigEncoded)
if err != nil {
return err
}
fmt.Println(searchres.Descriptor.Digest.String())
return nil
}
If I initialize client like this
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
This works because its using underlying docker daemon (in my case docker desktop) to query the registry. But if create client using
client.NewClientWithOpts(client.WithHost("https://hub.docker.com"), client.WithAPIVersionNegotiation())
it fails giving 404 error. Does this client require a docker daemon to work. If so, is there any other way i can query a registry? Please help me with this.
Skopeo is the leader software on dealing with registries without daemon.
It's also written with Go.
You can inspire from inspect.go
Note that, you don't need to use github.com/docker/docker/* modules, but it will be github.com/containers/*, and namely https://github.com/containers/image

Github Actions + docker-compose gives "No such file or directory"

I am writing CI for Github Actions with Rust. I want to execute docker-compose from Rust for some reasons. Within the Command, I am specifying a current working dir in the folder where the docker-compose.yml is located. This is some code:
let docker_compose_file = current_dir.parent().unwrap().to_owned();
if docker_compose_file.join("docker-compose.yml").exists() {
println!(
"Found docker-compose file, full path: {:#?}",
docker_compose_file
);
// DEBUG CODE
let ls_result = Command::new("ls")
.arg(docker_compose_file.to_str().unwrap())
.output()
.unwrap()
.stdout;
let y = String::from_utf8(ls_result).unwrap();
println!("LS'ing gives: {:#?}", y);
} else {
panic!(
"Wrong file, current working dir: {:#?}",
docker_compose_file
);
}
let result = Command::new("docker-compose")
.current_dir(&docker_compose_file)
.args(&["up", "-d"])
.status()
.unwrap();
I see in the Github Actions the following logging:
Found docker-compose file, full path:
"/Users/runner/work/something/something/server"
LS'ing gives:
"Cargo.lock\nCargo.toml\napi\nbatch_jobs\nci_setup\ncommon\ndatabase\ndeployment.md\ndocker-compose.yml\nrustfmt.toml\nserver\ntarget\n"
thread 'main' panicked at 'called Result::unwrap() on an Err
value: Os { code: 2, kind: NotFound, message: "No such file or
directory" }', ci_setup/src/main.rs:137:14 note: run with
RUST_BACKTRACE=1 environment variable to display a backtrace
I am confused. ls clearly shows me the docker-compose.yml file is present. Why do I get the Rust error, saying it can not find the file or directory?
main.rs:137 refers to the unwrap method call at the bottom of the code example.

not able to use packages in vendor directory in docker's repository

I am trying to create a container with docker's go api. I want to expose a port using container.Config.ExposedPorts in ContainerCreate()API. Below is the code
package main
import (
"fmt"
"context"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
)
func main() {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.WithVersion("1.38"))
if err != nil {
fmt.Println("Failed to get container envoronment", err)
}
resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: "hyperledger/fabric-ca",
Cmd: []string{"/bin/sh", "-c", "fabric-ca-server start -b admin:adminpw"},
Env: []string{"FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server",
"FABRIC_CA_SERVER_CA_NAME=ca.example.com"},
ExposedPorts: nat.PortSet{"22/tcp":struct{}{},},
}, nil, nil, "ca.example.com")
if err != nil {
fmt.Println(" failed to create container, err:", err)
} else {
fmt.Println(" Container ID :", resp.ID, "warning:", resp.Warnings, "err:", err)
}
}
when I compile I get the below error
vignesh#vignesh-ThinkPad-E470 ~/go-book/src/github.com/my_fabric $ go build asd.go
asd.go:8:9: cannot find package "github.com/docker/go-connections/nat" in any of:
/home/vignesh/go-book/src/github.com/my_fabric/vendor/github.com/docker/go-connections/nat (vendor tree)
/usr/local/go/src/github.com/docker/go-connections/nat (from $GOROOT)
/home/vignesh/go-book/src/github.com/docker/go-connections/nat (from $GOPATH)
As package "github.com/docker/go-connections/nat" is in a vendor directory at "github.com/docker/docker/vendor/github.com/docker/go-connections/nat", I then created a vendor directory in my working directory and copied contents of github.com/docker/docker/vendor/github.com/docker/go-connections/nat to github.com/my_fabric/vendor/go-connections/nat and used "github.com/my_fabric/go-connections/nat" in import rather than "github.com/docker/go-connections/nat". But I got the following error.
vignesh#vignesh-ThinkPad-E470 ~/go-book/src/github.com/my_fabric $ go build asd.go
# command-line-arguments
./asd.go:25:29: cannot use "github.com/my_fabric/vendor/github.com/my_fabric/go-connections/nat".PortSet literal (type "github.com/my_fabric/vendor/github.com/my_fabric/go-connections/nat".PortSet) as type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortSet in field value
Basically I want to use packages which is in vendor directory in docker's repository. Kindly help :)
It will work just try to provide user permissions to the vendor directory in docker environment. Beucase the vendor path to import the package is correct.
Import in golang works as:- First it will import the package from
vendor directory if not it will look for $GOPATH src directory for
the same package.
The error says it cannot find the package at any of the given paths. But you have it in the vendor directory so this can be any permission issue.
Since it happens if you are working on a linux the permissions will not allow to access the vendor directory.
Also it is better not to copy rather to generate a vendor package using Gopkg.toml in the docker.
Theses two directories are not equivalent:
github.com/docker/docker/vendor/github.com/docker/go-connections/nat
github.com/my_fabric/vendor/go-connections/nat
You have to move (not copy) all of Docker's vendored dependencies unchanged into your own vendor directory, e.g. the following directory should exist:
github.com/my_fabric/vendor/github.com/docker/go-connections/nat
Note the github.com/docker segments. If you copy the directories you'll end up with two copies of the packages, which will get you into trouble. For instance, you end up with the distinct types
"github.com/docker/docker/vendor/github.com/docker/go-connections/nat".Port
"github.com/docker/go-connections/nat".Port
You don't have to change your import statements at all.

Golang Docker API - Tail container logs

I'm using Go and hitting Docker's API to pull an image, and then create and execute a container. Specifically I'm using the docker-newman image. I can see that it's actually being executed in Kitematic, so I know that everything is setup correctly. However, my Go application isn't attaching and then tailing the logs being output.
I've seen this answer, and it references what appears to be the way to attach to the image and view the log. I can't seem to get it to work regardless of what I try.
attachToContainerOptions := docker.AttachToContainerOptions{
Container: container.ID,
OutputStream: os.Stdout,
ErrorStream: os.Stderr,
Logs: true,
Stdout: true,
Stderr: true,
}
if err := client.AttachToContainer(attachToContainerOptions); err != nil {
panic(err)
}
No error occurs, but this immediatley gets passed over without streaming anything to the console. How do I get this to stream to the console until the docker cmd completes?
Attach only works on a running container and the container lifetime for that image is ephemeral. Try *Client.Logs, instead, to get the resulting logs.
Here is a code sample:
logsOptions := docker.LogsOptions{
Container: container.ID,
OutputStream: os.Stdout,
ErrorStream: os.Stderr,
Follow: true,
Stdout: true,
Stderr: true,
}
if err := client.Logs(logsOptions); err != nil {
panic(err)
}

Docker Golang SDK - How to redirect container stdout to a file

Using the docker golang sdk the following method can be used to create a container and bind it's output to stdout.
resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: "alpine",
Cmd: []string{"echo", "Hello World"},
AttachStdout: true,
}, nil, nil, "")
How can I redirect this output to a file using the SDK ?
I'm using the official SDK of docker - github.com/docker/docker/client
You can use something like below
out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
if err != nil {
panic(err)
}
f, err := os.Create("/tmp/clogs")
io.Copy(f, out)
But make sure to to do that after you have started the container, Create will only create the container and not start it
The format of docker logs contains 8 bytes of header for each message, indicating for instance whether the output was on stdout or stderr. So one cannot simply copy the log output to a destination as Tarun Lalwani is mentioning in the other answer, because the header would then be interpreted as characters, garbling the output.
Unfortunately the client docs don't even mention the issue. This article explains it a bit and offers a library to solve the issue:
import (
"github.com/docker/docker/client"
"github.com/ahmetb/dlog"
)
// ---
reader, err := cli.ContainerLogs(ctx, resp.ID, nil)
if err != nil {
panic(err)
}
file, err := os.Create("/path/to/your/file")
io.Copy(file, dlog.NewReader(reader))

Resources