forked from adobe/buildrunner
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add config validation for multi-platform images
- Loading branch information
Shane Brown
committed
Aug 22, 2023
1 parent
3bb425d
commit e67fe09
Showing
5 changed files
with
344 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,150 @@ | ||
from re import L, T | ||
""" | ||
Copyright 2023 Adobe | ||
All Rights Reserved. | ||
NOTICE: Adobe permits you to use, modify, and distribute this file in accordance | ||
with the terms of the Adobe license agreement accompanying it. | ||
""" | ||
|
||
from typing import Optional | ||
from pydantic import BaseModel | ||
|
||
|
||
class StepBuild(BaseModel): | ||
""" Build model within a step """ | ||
path: Optional[str] | ||
dockerfile: Optional[str] | ||
pull: Optional[bool] | ||
platform: Optional[str] | ||
platforms: Optional[list[str]] | ||
|
||
class StepPush(BaseModel): | ||
|
||
class StepPushDict(BaseModel): | ||
""" Push model within a step """ | ||
repository: str | ||
tags: list[str] | ||
|
||
|
||
class Step(BaseModel): | ||
""" Step model """ | ||
build: Optional[StepBuild] | ||
push: Optional[StepPush | list[str | StepPush]] | ||
push: Optional[StepPushDict | list[str | StepPushDict] | str] | ||
|
||
def is_multi_platform(self): | ||
""" | ||
Check if the step is a multi-platform build step | ||
""" | ||
return self.build is not None and \ | ||
self.build.platforms is not None | ||
|
||
|
||
class Config(BaseModel): | ||
""" Top level config model """ | ||
version: Optional[float] | ||
steps: dict[str, Step] | ||
|
||
def __init__(self, **data) -> None: | ||
super().__init__(**data) | ||
self.validate() | ||
super().__init__(**data) | ||
self.validate() | ||
|
||
def has_multi_platform_build(self): | ||
""" | ||
Check if the config file has multi-platform build steps | ||
Returns: | ||
bool: True if the config file has multi-platform build steps, False otherwise | ||
""" | ||
for step in self.steps.values(): | ||
if step.build is not None and \ | ||
step.build.platforms is not None and \ | ||
isinstance(step.build.platforms, list): | ||
if step.is_multi_platform(): | ||
return True | ||
return False | ||
|
||
def validate_multi_platform_build(self): | ||
push_tags = {} | ||
print(self.steps) | ||
def validate_push(self, | ||
push: StepPushDict | list[str | StepPushDict] | str, | ||
mp_push_tags: set[str], | ||
step_name: str, | ||
update_mp_push_tags: bool = True): | ||
""" | ||
Validate push step | ||
Args: | ||
push (StepPushDict | list[str | StepPushDict] | str): Push step | ||
mp_push_tags (set[str]): Set of all tags used in multi-platform build steps | ||
step_name (str): Name of the step | ||
update_mp_push_tags (bool, optional): Whether to update the set of tags used in multi-platform steps. | ||
Raises: | ||
ValueError: If the config file is invalid | ||
""" | ||
# Check for valid push section, duplicate mp tags are not allowed | ||
if push is not None: | ||
name = None | ||
names = None | ||
if isinstance(push, str): | ||
name = push | ||
if ":" not in name: | ||
name = f'{name}:latest' | ||
|
||
if isinstance(push, StepPushDict): | ||
names = [f"{push.repository}:{tag}" for tag in push.tags] | ||
|
||
if names is not None: | ||
for current_name in names: | ||
if current_name in mp_push_tags: | ||
# raise ValueError(f'Cannot specify duplicate tag {current_name} in build step {step_name}') | ||
raise ValueError(f'Cannot specify duplicate tag {current_name} in build step {step_name}') | ||
|
||
if name is not None and name in mp_push_tags: | ||
# raise ValueError(f'Cannot specify duplicate tag {name} in build step {step_name}') | ||
raise ValueError(f'Cannot specify duplicate tag {name} in build step {step_name}') | ||
|
||
if update_mp_push_tags and names is not None: | ||
mp_push_tags.update(names) | ||
|
||
if update_mp_push_tags and name is not None: | ||
mp_push_tags.add(name) | ||
|
||
def validate_multi_platform_build(self, mp_push_tags: set[str]): | ||
""" | ||
Validate multi-platform build steps | ||
Args: | ||
mp_push_tags (set[str]): Set of all tags used in multi-platform build steps | ||
Raises: | ||
ValueError | pydantic.ValidationError: If the config file is invalid | ||
""" | ||
# Iterate through each step | ||
for step_name, step in self.steps.items(): | ||
print(step) | ||
if step.build is not None and \ | ||
step.build.platforms is not None: | ||
if step.is_multi_platform(): | ||
if step.build.platform is not None: | ||
raise ValueError(f'Cannot specify both platform ({step.build.platform}) and platforms ({step.build.platforms}) in build step {step_name}') | ||
# check for push tags | ||
# step.push | ||
return True | ||
return True | ||
raise ValueError(f'Cannot specify both platform ({step.build.platform}) and ' | ||
f'platforms ({step.build.platforms}) in build step {step_name}') | ||
|
||
if not isinstance(step.build.platforms, list): | ||
raise ValueError(f'platforms must be a list in build step {step_name}') | ||
|
||
# Check for valid push section, duplicate mp tags are not allowed | ||
self.validate_push(step.push, mp_push_tags, step_name) | ||
|
||
def validate(self): | ||
is_valid = True | ||
""" | ||
Validate the config file | ||
Raises: | ||
ValueError | pydantic.ValidationError : If the config file is invalid | ||
""" | ||
if self.has_multi_platform_build(): | ||
is_valid = self.validate_multi_platform_build() and is_valid | ||
mp_push_tags = set() | ||
self.validate_multi_platform_build(mp_push_tags) | ||
|
||
# Validate that all tags are unique across all multi-platform step | ||
for step_name, step in self.steps.items(): | ||
|
||
return is_valid | ||
# Check that there are no single platform tags that match multi-platform tags | ||
if not step.is_multi_platform(): | ||
if step.push is not None: | ||
self.validate_push(push=step.push, | ||
mp_push_tags=mp_push_tags, | ||
step_name=step_name, | ||
update_mp_push_tags=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.