2929from oras .schemas import manifest as oras_manifest_schema
3030
3131from gardenlinux .features import Parser
32- from ..constants import OCI_ANNOTATION_SIGNATURE_KEY , OCI_ANNOTATION_SIGNED_STRING_KEY
32+ from ..constants import (
33+ OCI_ANNOTATION_SIGNATURE_KEY ,
34+ OCI_ANNOTATION_SIGNED_STRING_KEY ,
35+ GL_USER_AGENT_REGISTRY ,
36+ )
3337from .checksum import (
3438 calculate_sha256 ,
3539 verify_sha256 ,
@@ -653,40 +657,59 @@ def push_from_dir(
653657 cname : str ,
654658 directory : str ,
655659 manifest_file : str ,
656- commit : Optional [ str ] = None ,
660+ additional_tags : list = None ,
657661 ):
658- # Step 1 scan and extract nested artifacts:
659- for file in os .listdir (directory ):
660- try :
661- if file .endswith (".pxe.tar.gz" ):
662- logger .info (f"Found nested artifact { file } " )
663- nested_tar_obj = tarfile .open (f"{ directory } /{ file } " )
664- nested_tar_obj .extractall (filter = "data" , path = directory )
665- nested_tar_obj .close ()
666- except (OSError , tarfile .FilterError , tarfile .TarError ) as e :
667- print (f"Failed to extract nested artifact { file } " , e )
668- exit (1 )
662+ """
663+ Push artifacts from a directory to a registry
664+
665+ Args:
666+ architecture: Target architecture of the image
667+ version: Version tag for the image
668+ cname: Canonical name of the image
669+ directory: Directory containing the artifacts
670+ manifest_file: File to write the manifest index entry to
671+ additional_tags: Additional tags to push the manifest with
672+
673+ Returns:
674+ The digest of the pushed manifest
675+ """
676+ if additional_tags is None :
677+ additional_tags = []
669678
670679 try :
680+ # Step 1: scan and extract nested artifacts
681+ for file in os .listdir (directory ):
682+ try :
683+ if file .endswith (".pxe.tar.gz" ):
684+ logger .info (f"Found nested artifact { file } " )
685+ nested_tar_obj = tarfile .open (f"{ directory } /{ file } " )
686+ nested_tar_obj .extractall (filter = "data" , path = directory )
687+ nested_tar_obj .close ()
688+ except (OSError , tarfile .FilterError , tarfile .TarError ) as e :
689+ print (f"Failed to extract nested artifact { file } " , e )
690+ exit (1 )
691+
692+ # Step 2: Get metadata from files
671693 oci_metadata = get_oci_metadata_from_fileset (
672694 os .listdir (directory ), architecture
673695 )
674696
675697 features = ""
698+ commit = ""
676699 for artifact in oci_metadata :
677700 if artifact ["media_type" ] == "application/io.gardenlinux.release" :
678- file = open (f"{ directory } /{ artifact ["file_name" ]} " , "r" )
679- lines = file . readlines ()
680- for line in lines :
681- if line . strip () .startswith ("GARDENLINUX_FEATURES=" ):
682- features = line .strip (). removeprefix (
683- "GARDENLINUX_FEATURES="
684- )
685- break
686- file . close ()
687-
688- flavor = Parser . get_flavor_from_cname ( cname , get_arch = True )
689-
701+ with open (f"{ directory } /{ artifact ["file_name" ]} " , "r" ) as file :
702+ for line in file :
703+ line = line . strip ()
704+ if line .startswith ("GARDENLINUX_FEATURES=" ):
705+ features = line .removeprefix ("GARDENLINUX_FEATURES=" )
706+ elif line . startswith ( "GARDENLINUX_COMMIT_ID=" ):
707+ commit = line . removeprefix ( "GARDENLINUX_COMMIT_ID=" )
708+ if features and commit : # Break if both values are found
709+ break
710+ break # Break after processing the release file
711+
712+ # Step 3: Push the image manifest
690713 digest = self .push_image_manifest (
691714 architecture ,
692715 cname ,
@@ -697,7 +720,116 @@ def push_from_dir(
697720 manifest_file ,
698721 commit = commit ,
699722 )
723+
724+ # Step 4: Process additional tags if provided
725+ if additional_tags and len (additional_tags ) > 0 :
726+ print (f"DEBUG: Processing { len (additional_tags )} additional tags" )
727+ logger .info (f"Processing { len (additional_tags )} additional tags" )
728+
729+ # Call push_additional_tags_manifest with repository information
730+ self .push_additional_tags_manifest (
731+ architecture ,
732+ cname ,
733+ version ,
734+ additional_tags ,
735+ container = self .container ,
736+ )
737+
738+ return digest
700739 except Exception as e :
701740 print ("Error: " , e )
702741 exit (1 )
703- return digest
742+
743+ def push_additional_tags_manifest (
744+ self , architecture , cname , version , additional_tags , container
745+ ):
746+ """
747+ Push additional tags for an existing manifest using ORAS Registry methods
748+
749+ Args:
750+ architecture: Target architecture of the image
751+ cname: Canonical name of the image
752+ version: Version tag for the image
753+ additional_tags: List of additional tags to push
754+ container: Container object
755+ """
756+ try :
757+ print (f"DEBUG: Processing { len (additional_tags )} additional tags for manifest" )
758+ print (f"DEBUG: Container: { container } " )
759+ print (f"DEBUG: Container api_prefix: { container .api_prefix } " )
760+ print (f"DEBUG: Container uri: { container .uri } " )
761+ print (f"DEBUG: Container tag: { container .tag } " )
762+ print (f"DEBUG: Container registry: { container .registry } " )
763+ print (f"DEBUG: Container repository: { container .repository } " )
764+
765+ # Source tag is the tag containing the version-cname-architecture combination
766+ source_tag = f"{ version } -{ cname } -{ architecture } "
767+ source_container = copy .deepcopy (container )
768+ source_container .tag = source_tag
769+
770+ # Authentication credentials from environment
771+ token = os .getenv ("GL_CLI_REGISTRY_TOKEN" )
772+ username = os .getenv ("GL_CLI_REGISTRY_USERNAME" )
773+ password = os .getenv ("GL_CLI_REGISTRY_PASSWORD" )
774+
775+ print (f"DEBUG: Token is { 'set' if token else 'not set' } " )
776+ print (f"DEBUG: Username is { 'set' if username else 'not set' } " )
777+ print (f"DEBUG: Password is { 'set' if password else 'not set' } " )
778+
779+ # Login to registry if credentials are provided
780+ if username and password :
781+ print (f"DEBUG: Logging in with username/password" )
782+ try :
783+ self .login (username , password )
784+ except Exception as login_error :
785+ print (f"DEBUG: Login error: { str (login_error )} " )
786+ elif token :
787+ # If token is provided, set it directly on the Registry instance
788+ print (f"DEBUG: Using token authentication" )
789+ self .token = base64 .b64encode (token .encode ("utf-8" )).decode ("utf-8" )
790+ self .auth .set_token_auth (self .token )
791+
792+ # Step 1: Get the manifest from the source container
793+ try :
794+ print (f"DEBUG: Getting manifest from { source_container } " )
795+ manifest = self .get_manifest (source_container )
796+ if not manifest :
797+ print (f"DEBUG: Failed to get manifest for { source_container } " )
798+ logger .error (f"Failed to get manifest for { source_container } " )
799+ return
800+ print (f"DEBUG: Successfully retrieved manifest: { manifest ['mediaType' ] if 'mediaType' in manifest else 'unknown' } " )
801+ except Exception as get_error :
802+ print (f"DEBUG: Error getting manifest: { str (get_error )} " )
803+ logger .error (f"Error getting manifest: { str (get_error )} " )
804+ return
805+
806+ # Step 2: For each additional tag, push the manifest using Registry.upload_manifest
807+ for tag in additional_tags :
808+ try :
809+ print (f"DEBUG: Pushing additional tag: { tag } " )
810+
811+ # Create a new container for this tag
812+ tag_container = copy .deepcopy (container )
813+ tag_container .tag = tag
814+
815+ print (f"DEBUG: Pushing to container: { tag_container } " )
816+
817+ # Upload the manifest to the new tag
818+ response = self .upload_manifest (manifest , tag_container )
819+
820+ if response and response .status_code in [200 , 201 ]:
821+ print (f"DEBUG: Successfully pushed tag { tag } for manifest" )
822+ logger .info (f"Successfully pushed tag { tag } for manifest" )
823+ else :
824+ status_code = getattr (response , 'status_code' , 'unknown' )
825+ response_text = getattr (response , 'text' , 'No response text' )
826+ print (f"DEBUG: Failed to push tag { tag } for manifest: { status_code } - { response_text } " )
827+ logger .error (f"Failed to push tag { tag } for manifest: { status_code } " )
828+
829+ except Exception as tag_error :
830+ print (f"DEBUG: Error pushing tag { tag } for manifest: { str (tag_error )} " )
831+ logger .error (f"Error pushing tag { tag } for manifest: { str (tag_error )} " )
832+
833+ except Exception as e :
834+ print (f"DEBUG: Error in push_additional_tags_manifest: { str (e )} " )
835+ logger .error (f"Error in push_additional_tags_manifest: { str (e )} " )
0 commit comments