Reputation: 13
My goal is to have a timer azure function (leveraging Selenium Chromedriver) run inside a docker container. it works in Visual Studio when i click this VS Docker Button; however, when i execute the same docker command from VS in the command line, nothing happens. here's the command VS executes
docker run -dt -v "C:\Users\rlin\vsdbg\vs2017u5:/remote_debugger:rw" -e "ASPNETCORE_ENVIRONMENT=Development" -p 34480:80 --name Retriever --entrypoint tail retriever -f /dev/null
here's my dockerfile for your reference
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/azure-functions/dotnet:3.0 AS base
WORKDIR /home/site/wwwroot
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /home/site/wwwroot
COPY . Retriever/
WORKDIR /home/site/wwwroot/Retriever
RUN dotnet build -c Release -o /home/site/wwwroot
FROM build AS publish
RUN dotnet publish -c Release -o /home/site/wwwroot
FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish /home/site/wwwroot .
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true \
AzureWebJobsStorage="StorageConnectionString"
URL="url" \
USERNAME="username" \
PASSWORD="password" \
SFTP="sftp" \
CONTAINER="container"
WORKDIR /home/site/wwwroot
RUN apt-get update && \
apt-get install -y gnupg wget curl unzip --no-install-recommends && \
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list && \
apt-get update -y && \
apt-get install -y google-chrome-stable && \
CHROMEVER=$(google-chrome --product-version | grep -o "[^\.]*\.[^\.]*\.[^\.]*") && \
DRIVERVER=$(curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROMEVER") && \
wget -q --continue -P /chromedriver "http://chromedriver.storage.googleapis.com/$DRIVERVER/chromedriver_linux64.zip" && \
unzip /chromedriver/chromedriver* -d /chromedriver
WORKDIR /
CMD ["/home/site/wwwroot/bin/Retriever.dll"]
been stuck on this for days, so any help is much appreciated.
EDIT: here's the code for ref
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OpenQA.Selenium.Chrome;
using Microsoft.Extensions.Configuration;
using OpenQA.Selenium;
using System.Collections.Generic;
using Azure.Storage.Blobs;
using System.IO.Compression;
namespace Retriever
{
public class Retriever
{
private IConfigurationRoot config;
[FunctionName("Retriever")]
public void Run(
[TimerTrigger("0 */1 * * * *", RunOnStartup = true)] TimerInfo myTimer,
//[HttpTrigger(AuthorizationLevel.System, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Retrieve(log);
log.LogInformation("Uploaded attachments");
//return new OkResult();
}
public void Retrieve(ILogger log)
{
log.LogInformation("Retrieving attachments");
ChromeOptions options = new ChromeOptions();
options.AddArgument("--headless");
options.AddArgument("--no-sandbox"); // Bypass OS security model
options.AddArgument("start-maximized"); // open Browser in maximized mode
options.AddArgument("disable-infobars"); // disabling infobars
options.AddArgument("--disable-extensions"); // disabling extensions
options.AddArgument("--disable-gpu"); // applicable to windows os only
options.AddArgument("--disable-dev-shm-usage"); // overcome limited resource problems
options.AddArgument("--whitelisted-ips");
options.AddArgument("--proxy-server=direct://");
options.AddArgument("--proxy-bypass-list=*");
options.BinaryLocation = "/opt/google/chrome/google-chrome"; //Google Chrome path
options.AddUserProfilePreference("download.prompt_for_download", false); // DO NOT prompt for download
options.AddUserProfilePreference("download.default_directory", Path.GetTempPath()); // saves to Temp directory
ChromeDriverService service = ChromeDriverService.CreateDefaultService("/chromedriver/", "chromedriver"); // use "/usr/bin/" and "chromedriver" in deployment
ChromeDriver driver = new ChromeDriver(service, options, TimeSpan.FromMinutes(3));
driver.Manage().Timeouts().ImplicitWait = new TimeSpan(5, 0, 0);
// Login and Download Steps //
driver.Navigate().GoToUrl(config.GetValue<string>("URL"));
IWebElement username = driver.FindElement(By.XPath("//*[@id=\"username\"]"));
username.SendKeys(config.GetValue<string>("USERNAME"));
IWebElement next = driver.FindElement(By.CssSelector("body > mc-login > div > div.container > div > div > div.panel.panel-default.panel-shadow.login-panel > div > div > div > form > button"));
next.Click();
IWebElement password = driver.FindElement(By.CssSelector("#password"));
password.SendKeys(config.GetValue<string>("PASSWORD"));
IWebElement login = driver.FindElement(By.CssSelector("body > mc-login > div > div.container > div > div > div.panel.panel-default.panel-shadow.login-panel > div > div > div > form > button"));
login.Click();
IWebElement inbox = driver.FindElement(By.LinkText("Inbox"));
inbox.Click();
ICollection<IWebElement> emails = driver.FindElements(By.CssSelector("tr"));
System.Threading.Thread.Sleep(new TimeSpan(0, 0, 2));
foreach(IWebElement email in emails)
{
if (email.Displayed)
{
email.Click();
IWebElement view = driver.FindElement(By.LinkText("View"));
view.Click();
string filename = driver.FindElement(By.CssSelector("body > div.page-container.with-sidebar.full-height.snap-content > div.main-content.full-height > div.ng-isolate-scope > div > div > div.container-fluid.full-height.fill-container.mainarea.ng-scope.mc-tab-unique-main > mc-list-detail > div > div.col-xs-12.col-sm-7.col-md-8.col-lg-8.hidden-xs.full-height.animate-active.no-padding > div > div.dynamic-full-height.horizontal-scroll.vertical-scroll.panel-half-margin-top-negative.ng-scope > div.pull-left.full-width.ng-scope > detail > div:nth-child(4) > div > mc-thumbnail > div > div:nth-child(2) > ul > li > span > span:nth-child(2)")).GetAttribute("innerHTML");
string filepath = Path.Combine(Path.GetTempPath(), filename);
IWebElement download = driver.FindElement(By.LinkText("Download"));
download.Click();
System.Threading.Thread.Sleep(new TimeSpan(0, 0, 2));
UploadToBlobStorage(log, filepath, filename);
}
}
driver.Close();
driver.Quit();
driver.Dispose();
service.Dispose();
}
public void UploadToBlobStorage(ILogger log, string filepath, string filename)
{
BlobServiceClient serviceClient = new BlobServiceClient(config.GetValue<string>("SFTP"));
BlobContainerClient containerClient = serviceClient.GetBlobContainerClient(config.GetValue<string>("CONTAINER"));
BlobClient client = containerClient.GetBlobClient("uploads/" + filename + ".txt");
ZipArchive z = ZipFile.Open(filepath, ZipArchiveMode.Update);
foreach(ZipArchiveEntry e in z.Entries)
{
client.Upload(e.Open(), overwrite: true);
}
z.Dispose();
if(File.Exists(filepath)) { File.Delete(filepath); }
}
}
}
Upvotes: 1
Views: 2169
Reputation: 136
The command you posted is only used for Visual Studios debugging functionality. It overwites the entrypoint with tail -f /dev/null
which is basically "loop forever".
You will need to run docker build <path to dir where Dockerfile is located> -t retriever
to build the image and then docker run -p 80:80 retriever
to run it. This should make the app available on port 80 of your system. If you want the app on another port, you can use -p <another port>:80
The last line (CMD ["/home/site/wwwroot/bin/Retriever.dll"]
) in your Dockerfile overwrites the preset CMD of azure-functions/dotnet
(/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost
) and will lead to the error "exec: \"dotnet\": executable file not found in $PATH"
. Your function is supposed to be run by WebHost
, not by invoking the dll directly. Remove this line. The one above it is also unnecessary.
Your Dockerfile
also has another issue (it will possibly still work without adressing them):
You are copying your sources, building and publishing all to the same directory /home/site/wwwroot
. I would suggest changing it like this for the build stage:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY . .
RUN dotnet build -c Release -o /app/build
For the publish stage:
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish
And for final:
FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish /app/publish .
[...]
Upvotes: 1