11# -*- coding: utf-8 -*-
22
33import base64
4+ import configparser
45import copy
56import hashlib
67import json
3031
3132from ..constants import OCI_ANNOTATION_SIGNATURE_KEY , OCI_ANNOTATION_SIGNED_STRING_KEY
3233from ..features import CName
33-
3434from .checksum import (
3535 calculate_sha256 ,
3636 verify_sha256 ,
@@ -673,38 +673,73 @@ def push_from_dir(
673673 cname : str ,
674674 directory : str ,
675675 manifest_file : str ,
676- commit : Optional [ str ] = None ,
676+ additional_tags : list = None ,
677677 ):
678- # Step 1 scan and extract nested artifacts:
679- for file in os .listdir (directory ):
680- try :
681- if file .endswith (".pxe.tar.gz" ):
682- logger .info (f"Found nested artifact { file } " )
683- nested_tar_obj = tarfile .open (f"{ directory } /{ file } " )
684- nested_tar_obj .extractall (filter = "data" , path = directory )
685- nested_tar_obj .close ()
686- except (OSError , tarfile .FilterError , tarfile .TarError ) as e :
687- print (f"Failed to extract nested artifact { file } " , e )
688- exit (1 )
678+ """
679+ Push artifacts from a directory to a registry
680+
681+ Args:
682+ architecture: Target architecture of the image
683+ version: Version tag for the image
684+ cname: Canonical name of the image
685+ directory: Directory containing the artifacts
686+ manifest_file: File to write the manifest index entry to
687+ additional_tags: Additional tags to push the manifest with
688+
689+ Returns:
690+ The digest of the pushed manifest
691+ """
692+ if additional_tags is None :
693+ additional_tags = []
689694
690695 try :
696+ # scan and extract nested artifacts
697+ for file in os .listdir (directory ):
698+ try :
699+ if file .endswith (".pxe.tar.gz" ):
700+ logger .info (f"Found nested artifact { file } " )
701+ nested_tar_obj = tarfile .open (f"{ directory } /{ file } " )
702+ nested_tar_obj .extractall (filter = "data" , path = directory )
703+ nested_tar_obj .close ()
704+ except (OSError , tarfile .FilterError , tarfile .TarError ) as e :
705+ print (f"Failed to extract nested artifact { file } " , e )
706+ exit (1 )
707+
708+ # Get metadata from files
691709 oci_metadata = get_oci_metadata_from_fileset (
692710 os .listdir (directory ), architecture
693711 )
694712
695713 features = ""
714+ commit = ""
696715 for artifact in oci_metadata :
697716 if artifact ["media_type" ] == "application/io.gardenlinux.release" :
698- file = open (f"{ directory } /{ artifact ["file_name" ]} " , "r" )
699- lines = file .readlines ()
700- for line in lines :
701- if line .strip ().startswith ("GARDENLINUX_FEATURES=" ):
702- features = line .strip ().removeprefix (
703- "GARDENLINUX_FEATURES="
717+ try :
718+ file_path = f"{ directory } /{ artifact ['file_name' ]} "
719+
720+ config = configparser .ConfigParser (allow_unnamed_section = True )
721+ config .read (file_path )
722+
723+ if config .has_option (
724+ configparser .UNNAMED_SECTION , "GARDENLINUX_FEATURES"
725+ ):
726+ features = config .get (
727+ configparser .UNNAMED_SECTION , "GARDENLINUX_FEATURES"
704728 )
705- break
706- file .close ()
729+ if config .has_option (
730+ configparser .UNNAMED_SECTION , "GARDENLINUX_COMMIT_ID"
731+ ):
732+ commit = config .get (
733+ configparser .UNNAMED_SECTION , "GARDENLINUX_COMMIT_ID"
734+ )
735+
736+ except (configparser .Error , IOError ) as e :
737+ logger .error (
738+ f"Error reading config file { artifact ['file_name' ]} : { e } "
739+ )
740+ break
707741
742+ # Push the image manifest
708743 digest = self .push_image_manifest (
709744 architecture ,
710745 cname ,
@@ -715,7 +750,103 @@ def push_from_dir(
715750 manifest_file ,
716751 commit = commit ,
717752 )
753+
754+ # Process additional tags if provided
755+ if additional_tags and len (additional_tags ) > 0 :
756+ print (f"DEBUG: Processing { len (additional_tags )} additional tags" )
757+ logger .info (f"Processing { len (additional_tags )} additional tags" )
758+
759+ self .push_additional_tags_manifest (
760+ architecture ,
761+ cname ,
762+ version ,
763+ additional_tags ,
764+ container = self .container ,
765+ )
766+
767+ return digest
718768 except Exception as e :
719769 print ("Error: " , e )
720770 exit (1 )
721- return digest
771+
772+ def push_additional_tags_manifest (
773+ self , architecture , cname , version , additional_tags , container
774+ ):
775+ """
776+ Push additional tags for an existing manifest using ORAS Registry methods
777+
778+ Args:
779+ architecture: Target architecture of the image
780+ cname: Canonical name of the image
781+ version: Version tag for the image
782+ additional_tags: List of additional tags to push
783+ container: Container object
784+ """
785+ try :
786+ # Source tag is the tag containing the version-cname-architecture combination
787+ source_tag = f"{ version } -{ cname } -{ architecture } "
788+ source_container = copy .deepcopy (container )
789+ source_container .tag = source_tag
790+
791+ # Authentication credentials from environment
792+ token = os .getenv ("GL_CLI_REGISTRY_TOKEN" )
793+ username = os .getenv ("GL_CLI_REGISTRY_USERNAME" )
794+ password = os .getenv ("GL_CLI_REGISTRY_PASSWORD" )
795+
796+ # Login to registry if credentials are provided
797+ if username and password :
798+ logger .debug (f"Logging in with username/password" )
799+ try :
800+ self .login (username , password )
801+ except Exception as login_error :
802+ logger .error (f"Login error: { str (login_error )} " )
803+ elif token :
804+ # If token is provided, set it directly on the Registry instance
805+ logger .debug (f"Using token authentication" )
806+ self .token = base64 .b64encode (token .encode ("utf-8" )).decode ("utf-8" )
807+ self .auth .set_token_auth (self .token )
808+
809+ # Get the manifest from the source container
810+ try :
811+ logger .debug (f"Getting manifest from { source_container } " )
812+ manifest = self .get_manifest (source_container )
813+ if not manifest :
814+ logger .error (f"Failed to get manifest for { source_container } " )
815+ return
816+ logger .info (
817+ f"Successfully retrieved manifest: { manifest ['mediaType' ] if 'mediaType' in manifest else 'unknown' } "
818+ )
819+ except Exception as get_error :
820+ logger .error (f"Error getting manifest: { str (get_error )} " )
821+ return
822+
823+ # For each additional tag, push the manifest using Registry.upload_manifest
824+ for tag in additional_tags :
825+ try :
826+ logger .debug (f"Pushing additional tag: { tag } " )
827+
828+ # Create a new container for this tag
829+ tag_container = copy .deepcopy (container )
830+ tag_container .tag = tag
831+
832+ logger .debug (f"Pushing to container: { tag_container } " )
833+
834+ # Upload the manifest to the new tag
835+ response = self .upload_manifest (manifest , tag_container )
836+
837+ if response and response .status_code in [200 , 201 ]:
838+ logger .info (f"Successfully pushed tag { tag } for manifest" )
839+ else :
840+ status_code = getattr (response , "status_code" , "unknown" )
841+ response_text = getattr (response , "text" , "No response text" )
842+ logger .error (
843+ f"Failed to push tag { tag } for manifest: { status_code } "
844+ )
845+
846+ except Exception as tag_error :
847+ logger .error (
848+ f"Error pushing tag { tag } for manifest: { str (tag_error )} "
849+ )
850+
851+ except Exception as e :
852+ logger .error (f"Error in push_additional_tags_manifest: { str (e )} " )
0 commit comments