-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate Azure Functions deployment with In-Vitro #569
base: main
Are you sure you want to change the base?
Conversation
Looks like there is an error in the checks.
You can find more details about these requirements in our contributing guide: |
f717c64
to
bae6305
Compare
Hi @leokondrashov, I've completed my initial review of this PR. @cavinkavi has made the corresponding changes, and the current version looks good to me. Do you have any additional feedback or concerns? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, there are no tests for the infrastructure, please add. Better even add them into CI tests.
Second, does Azure support Go deployments? We don't use Python that much and I'm not sure that our main Python implementation is well-behaved. It would be preferable to use Go instead.
# Simulate the busySpin function | ||
def busy_spin(duration_ms: int) -> None: | ||
end_time = perf_counter() + duration_ms / 1000 # Convert ms to seconds | ||
while perf_counter() < end_time: | ||
continue | ||
|
||
# Convert TraceFunctionExecution | ||
def trace_function_execution(start: float, time_left_milliseconds: int) -> str: | ||
time_consumed_milliseconds = int((time.time() - start) * 1000) | ||
if time_consumed_milliseconds < time_left_milliseconds: | ||
time_left_milliseconds -= time_consumed_milliseconds | ||
if time_left_milliseconds > 0: | ||
busy_spin(time_left_milliseconds) | ||
|
||
return f"OK - {hostname}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please reuse this parts from existing Python implementation (server/trace-func-py/trace_func.py
). Don't reimplement because this creates duplicates in the code and incorrect performance comparisons.
No need to throw out the Python deployment, though. It could be an option to deploy one of them during runtime. But the default should be Go to align with other deployments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, please change the message of your commit to something meaningful. Addressing my comments doesn't make any sense when you are looking at the history of commits.
} | ||
|
||
// Helper function to copy files | ||
func copyFile(src, dst string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put this function in utils
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you have the end-to-end test. Please add a test that would check the potential zip file for being operational. This wouldn't require the Azure credentials, so we can run it without any concerns about spending. This test would show when we have problems with our file copies in the archive.
Please also add them to the CI, so we can see the results right in the PRs.
At the same time, we can have an end-to-end test by just running the loader.go
with the correct config file and checking for the output. There is no reason to add the script that duplicates the experiment pipeline. Better to repurpose it to test specific parts of the pipeline (deploying/cleaning functions, checking for the zip health).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, would it be better if I remove the azure_functions_test.go file and create an End-to-End test under workflows, which will run the go command and check the output as you have mentioned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, similar to existing e2e tests. But still, add the module tests that I mentioned. For them, you would need the *_test.go files. And don't forget to add them into CI.
9fc7f13
to
528ebf3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And still, the commit name doesn't make sense. In this PR, you introduce the Azure Function deployment. Please put this as the first line of the commit message (and the only, I don't see the reason to put any other comments there apart from sign-offs, feel free to disagree).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add separate CI for that
.github/workflows/e2e_azure.yaml
Outdated
- name: Install Golang | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: 1.21 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look at other CI workflows and use same version (AWS e2e is outdated as well but we need to rework whole that part)
.github/workflows/e2e_azure.yaml
Outdated
- name: Run Zip File Health Test | ||
run: go test -v ./pkg/driver/deployment/azure_functions_test.go -run TestZipHealth | ||
|
||
- name: Run Cleanup Test | ||
run: go test -v ./pkg/driver/deployment/azure_functions_test.go -run TestCleanup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be separate CI, not combined with e2e.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add the unit test for deployment logic. I would want to see an easy troubleshooting route for failures of e2e test. Unit tests should cover most of the steps separately so we would see which ones fail and work on that instead of going into logs of e2e.
def extract_execute_function(src_path): | ||
# Extract the execute_function logic from trace_func.py. | ||
with open(src_path, "r") as f: | ||
content = f.read() | ||
|
||
# Use regex to extract the execute_function definition and body | ||
match = re.search(r"def execute_function\(.*?\):.*?(?=def |\Z)", content, re.DOTALL) | ||
if not match: | ||
raise ValueError("execute_function() not found in trace_func.py") | ||
|
||
return match.group(0) | ||
|
||
def inject_function(func_code, workload_path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how this slipped from the previous review.
Please don't look inside the file and parse it to extract the implementation during deployment. What I proposed is to pull this function into a separate file and reuse it from both places. This way, during deployment, you only need to copy the file and import. I don't trust parsing the files, regexp are not as robust as you would think.
docs/loader.md
Outdated
```bash | ||
az login | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not always possible. There are no browser available on cloudlab nodes. Can you provide pure CLI method of logging in?
Signed-off-by: Kavithran <104263022+cavinkavi@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is exactly the same as in the server/trace-func-py. Please leave only one of them.
And add the other one in .gitignore file so we won't see it added in git status
after we do deployment. Also add the note in the main py file for Azure deployment near the execute_function call about the fact that you need to copy it for local development.
Notes: | ||
|
||
- Service Principal must be created before running experiment, as some environments do not have browsers (e.g. CloudLab). Perform these steps in an environment that allows launching of browser and use the generated credentials. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Service Principal must be created before running experiment, as some environments do not have browsers (e.g. CloudLab). Perform these steps in an environment that allows launching of browser and use the generated credentials. | |
- Service Principal must be created before running the experiment, as some environments do not GUI (e.g., CloudLab nodes). You can perform these steps in an environment that allows the launching of the browser and use the generated credentials. |
Overall, can this be done via Azure web console? This is much simpler than doing it via CLI, since this part you don't need to do in experiment environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of whole this file, you can add the directory into modules to test in unit-tests.yaml
. Either way, you run all of the tests that you wrote in azure_functions_test.go but you specify all of them one-by-one, which is prone to errors (add test, forgot to test or the opposite, it fails because you removed one).
func mockExecCommand(output string, err error) deployment.CommandRunner { | ||
return func(name string, arg ...string) *exec.Cmd { | ||
cmd := exec.Command("echo", output) // Simulate success | ||
if err != nil { | ||
return exec.Command("false") // Simulate failure | ||
} | ||
return cmd | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this thing do? I only see it used once with err=nil
. It overloads the code with unnecessary flexibility.
assert.Contains(t, logs, "Deployed all 2 functions successfully") | ||
} | ||
|
||
// Tests CleanUpDeploymentFiles function correctly removes the specified temporary files and directories. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test tests just two rm commands. It seems excessive for me. When I was talking about cleanup I meant the cleaning up the deployment in Azure.
} | ||
|
||
// Tests DeployFunction function by simulating deployment of zip file to Azure. | ||
func TestDeployFunction(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test just deploys the functions without cleaning them. So, my guess is that it fails on the second time onwards because functions already exist. Please test cleaning them up in the same test, so we can see whether it works. Also, be careful with the collision of tests. I.e., you shouldn't mistakenly remove the e2e functions that happen to run at the same time.
|
||
func ZipFunctionAppFiles() error { | ||
// Use bash to zip the contents of azure_functions_for_zip/ along with host.json and requirements.txt | ||
cmd := exec.Command("bash", "-c", "cd azure_functions_for_zip && zip -r ../azurefunctions.zip . && cd .. && zip -j azurefunctions.zip azurefunctions_setup/host.json azurefunctions_setup/requirements.txt") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add all of the temporary directories and files into .gitignore
|
||
// Deploy the zip file to Azure Function App using CLI | ||
cmd := runner("az", "functionapp", "deployment", "source", "config-zip", | ||
"--name", config.AzureConfig.FunctionAppName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an issue with deploying all functions into the same application. In Azure, the scaling unit is an application, so when you invoke one of the functions, it will instantiate all of the functions in the app. So, if you call other functions in the application, it would be a warm start instead of a cold start. We should model independent scaling for all functions, so they should be in different applications.
|
||
/* Functions for deploying zipped functions */ | ||
|
||
func DeployFunction(config *Config, function []*common.Function, runner CommandRunner) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, this name is confusing, it deploys all the functions, not just one
Summary
A small summary of the requirements (in one/two sentences).
To integrate Azure Functions deployment with In-Vitro. This feature enables deployment of serverless functions on the Azure Functions platform.
Implementation Notes ⚒️
This PR adds a workflow to deploy multiple functions to Azure Functions via ZIP deployment.
Documentation on the steps to deploy the functions to Azure are included in
loader.md
.azurefunctions_setup
: this folder contains the relevant config and workload files to be used for deployment.azure_functions.go
: initializes Azure resources required for deployment to Azure Functions, such as Resource Group, Storage Account, and Function App. In order to deploy multiple functions via ZIP, each function must have its own specific folder containing required dependencies, before zipping and deploying all the function folders as a single package. After successful deployment, local temporary folders holding the functions and the zip package are cleaned up.(Note: All cleanup, including local temporary folders, will be done after entire experiment is completed.)
External Dependencies 🍀
Breaking API Changes⚠️
Simply specify none (N/A) if not applicable.