Skip to content
This repository has been archived by the owner on Nov 20, 2023. It is now read-only.

Fixed when path to Dockerfile isn't correctly expanded when service is references as repository. #1083

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

OlegKarasik
Copy link
Contributor

Hello,

While experimenting with tye I have found a bug related to correct path of Dockerfile based services referenced as repository. Here is how I reproduced the issue:

  1. Create a repository remote. In this repository create an API service remote-api, create a Dockerfile for it and create the following tye.yaml:
name: remote-service
services:
- name: remote-api
  dockerFile: remote-api/Dockerfile
  bindings:
    - protocol: http
      containerPort: 5000

If you run any tye command (build, run, etc) it will work fine. So the configuration is correct. Now create a one more repository source. In this repository create an API service source-api and create the following tye.yaml:

name: source-service
services:
- name: source-api
  project: source-api/source-api.csproj
- name: remote-api
  repository: <path to repository>

Now if you run tye build you will receive the following error:

An unhandled exception has occurred, how unseemly: 
System.ComponentModel.Win32Exception (267): The directory name is invalid.
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at System.CommandLine.Invocation.Process.StartProcess(String command, String args, String workingDir, Action`1 stdOut, Action`1 stdErr, ValueTuple`2[] environmentVariables)
   at System.CommandLine.Invocation.Process.ExecuteAsync(String command, String args, String workingDir, Action`1 stdOut, Action`1 stdErr, ValueTuple`2[] environmentVariables)
   at Microsoft.Tye.DockerContainerBuilder.BuildContainerImageFromDockerFileAsync(OutputContext output, ApplicationBuilder application, DockerFileServiceBuilder containerService, ContainerInfo container) in /_/src/Microsoft.Tye.Core/DockerContainerBuilder.cs:line 46
   at Microsoft.Tye.BuildDockerImageStep.ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service) in /_/src/Microsoft.Tye.Core/BuildDockerImageStep.cs:line 39
   at Microsoft.Tye.ApplicationExecutor.ExecuteAsync(ApplicationBuilder application) in /_/src/Microsoft.Tye.Core/ApplicationExecutor.cs:line 37
   at Microsoft.Tye.BuildHost.ExecuteBuildAsync(OutputContext output, ApplicationBuilder application, String environment, Boolean interactive) in /_/src/tye/BuildHost.cs:line 40
   at Microsoft.Tye.BuildHost.BuildAsync(OutputContext output, FileInfo path, Boolean interactive, String framework, ApplicationFactoryFilter filter) in /_/src/tye/BuildHost.cs:line 22
   at System.CommandLine.Invocation.CommandHandler.GetResultCodeAsync(Object value, InvocationContext context)
   at System.CommandLine.Invocation.ModelBindingCommandHandler.InvokeAsync(InvocationContext context)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseParseErrorReporting>b__19_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass17_0.<<UseMiddleware>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass14_0.<<UseHelp>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass22_0.<<UseVersionOption>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseDebugDirective>b__10_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass12_0.<<UseExceptionHandler>b__0>d.MoveNext()

Running tye build -v Debug reveals the reason:

Running 'docker build'.
        > docker build "D:\Projects\Bug\remote-api" -t remote-api:latest -f "D:\Projects\Bug\remote-api\Dockerfile"

However, the path should be D:\Projects\Bug\.tye\deps\remote\remote-api because remote repository is downloaded into deps directory.

To fix this issue I have separated the logic of FullPath resolutions (previously it was done only for .csproj files as part of project evaluation) into separate method. Added a DockerFileFullPath property to ConfigService and implemented the same resolution for it as it was previously done for ProjectFileFullPath.

Hope this PR will be useful.

@davidfowl
Copy link
Member

Any way to test this?

@OlegKarasik
Copy link
Contributor Author

Any way to test this?

Yes.

You can checkout this repository (I used it in one of my presentations, so please ignore the things written in README.md), navigate to solution-3 directory and execute tye run command.

You can at first run it with normal tye and then with the fix. In the first case you should receive the above error and in case of the fix, everything should be fine.

@davidfowl
Copy link
Member

I meant an automated test as part of this PR

@OlegKarasik
Copy link
Contributor Author

I meant an automated test as part of this PR

No, currently there is no such test included in a PR but I can take a look on how to implement in.

@OlegKarasik
Copy link
Contributor Author

I see the automated tests in the repository (there were none at the time of PR). I will dig into how to create one for my case.

@OlegKarasik
Copy link
Contributor Author

@davidfowl I have implemented the automated test MultiRepo_WorksWithCloningAndDockerfile and verified it fails in main but works after the fix.

@davidfowl
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

name: tye-docker-sample
services:
- name: minapp
repository: https://github.com/OlegKarasik/tye-docker-sample";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic dependency but I'll merge this fix for now. If this repo disappears this test will start breaking @OlegKarasik

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidfowl, I understand the issue with the repo. I personally have no plans to remove it but in any case, may be there is a way to fork it into more persistent (and maintainable by the team) place? I am good with this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's possible to do this within the tye repo itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidfowl, I don't think this is possible without introducing changes to how tye searches for configuration files. Currently this is done in the ConfigFileFinder class, which does search in the root of the passed directory:

public static bool TryFindSupportedFile(
  string directoryPath, 
  [NotNullWhen(true)] out string? filePath, 
  [MaybeNullWhen(true)] out string? errorMessage, 
  string[]? fileFormats = null)
{
    fileFormats ??= FileFormats;
    foreach (var format in fileFormats)
    {
        var files = Directory.GetFiles(directoryPath, format);

        switch (files.Length)
        {
            case 1:
                errorMessage = null;
                filePath = files[0];
                return true;
            case 0:
                continue;
        }

        errorMessage = $"More than one matching file was found in directory '{directoryPath}'.";
        filePath = default;
        return false;
    }

    errorMessage = $"No project project file or solution was found in directory '{directoryPath}'.";
    filePath = default;
    return false;
}

This method is used to find a configuration file in the directory of cloned repository inside ApplicationFactory class:

var clonePath = Path.Combine(rootConfig.Source.DirectoryName!, path, configService.Name);

if (!Directory.Exists(clonePath))
{
  if (!await GitDetector.Instance.IsGitInstalled.Value)
  {
      throw new CommandException($"Cannot clone repository {configService.Repository} because git is not installed. Please install git if you'd like to use \"repository\" in tye.yaml.");
  }

  var result = await ProcessUtil.RunAsync("git", $"clone {configService.Repository} \"{clonePath}\"", workingDirectory: rootConfig.Source.DirectoryName, throwOnError: false);

  if (result.ExitCode != 0)
  {
      throw new CommandException($"Failed to clone repository {configService.Repository} with exit code {result.ExitCode}.{Environment.NewLine}{result.StandardError}{result.StandardOutput}.");
  }
}

if (!ConfigFileFinder.TryFindSupportedFile(clonePath, out var file, out var errorMessage))
{
  throw new CommandException(errorMessage!);
}

@OlegKarasik
Copy link
Contributor Author

Good morning, Are there any changes left to be done before the PR can be merged?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants