github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/builder/writer/structured_format.go (about)

     1  package writer
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/buildpacks/pack/internal/style"
     7  	"github.com/buildpacks/pack/pkg/client"
     8  
     9  	pubbldr "github.com/buildpacks/pack/builder"
    10  	"github.com/buildpacks/pack/internal/builder"
    11  	"github.com/buildpacks/pack/internal/config"
    12  	"github.com/buildpacks/pack/pkg/dist"
    13  	"github.com/buildpacks/pack/pkg/logging"
    14  )
    15  
    16  type InspectOutput struct {
    17  	SharedBuilderInfo
    18  	RemoteInfo *BuilderInfo `json:"remote_info" yaml:"remote_info" toml:"remote_info"`
    19  	LocalInfo  *BuilderInfo `json:"local_info" yaml:"local_info" toml:"local_info"`
    20  }
    21  
    22  type RunImage struct {
    23  	Name           string `json:"name" yaml:"name" toml:"name"`
    24  	UserConfigured bool   `json:"user_configured,omitempty" yaml:"user_configured,omitempty" toml:"user_configured,omitempty"`
    25  }
    26  
    27  type Lifecycle struct {
    28  	builder.LifecycleInfo `yaml:"lifecycleinfo,inline"`
    29  	BuildpackAPIs         builder.APIVersions `json:"buildpack_apis" yaml:"buildpack_apis" toml:"buildpack_apis"`
    30  	PlatformAPIs          builder.APIVersions `json:"platform_apis" yaml:"platform_apis" toml:"platform_apis"`
    31  }
    32  
    33  type Stack struct {
    34  	ID     string   `json:"id" yaml:"id" toml:"id"`
    35  	Mixins []string `json:"mixins,omitempty" yaml:"mixins,omitempty" toml:"mixins,omitempty"`
    36  }
    37  
    38  type BuilderInfo struct {
    39  	Description            string                  `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"`
    40  	CreatedBy              builder.CreatorMetadata `json:"created_by" yaml:"created_by" toml:"created_by"`
    41  	Stack                  *Stack                  `json:"stack,omitempty" yaml:"stack,omitempty" toml:"stack,omitempty"`
    42  	Lifecycle              Lifecycle               `json:"lifecycle" yaml:"lifecycle" toml:"lifecycle"`
    43  	RunImages              []RunImage              `json:"run_images" yaml:"run_images" toml:"run_images"`
    44  	Buildpacks             []dist.ModuleInfo       `json:"buildpacks" yaml:"buildpacks" toml:"buildpacks"`
    45  	pubbldr.DetectionOrder `json:"detection_order" yaml:"detection_order" toml:"detection_order"`
    46  	Extensions             []dist.ModuleInfo      `json:"extensions,omitempty" yaml:"extensions,omitempty" toml:"extensions,omitempty"`
    47  	OrderExtensions        pubbldr.DetectionOrder `json:"order_extensions,omitempty" yaml:"order_extensions,omitempty" toml:"order_extensions,omitempty"`
    48  }
    49  
    50  type StructuredFormat struct {
    51  	MarshalFunc func(interface{}) ([]byte, error)
    52  }
    53  
    54  func (w *StructuredFormat) Print(
    55  	logger logging.Logger,
    56  	localRunImages []config.RunImage,
    57  	local, remote *client.BuilderInfo,
    58  	localErr, remoteErr error,
    59  	builderInfo SharedBuilderInfo,
    60  ) error {
    61  	if localErr != nil {
    62  		return fmt.Errorf("preparing output for %s: %w", style.Symbol(builderInfo.Name), localErr)
    63  	}
    64  
    65  	if remoteErr != nil {
    66  		return fmt.Errorf("preparing output for %s: %w", style.Symbol(builderInfo.Name), remoteErr)
    67  	}
    68  
    69  	outputInfo := InspectOutput{SharedBuilderInfo: builderInfo}
    70  
    71  	if local != nil {
    72  		var stack *Stack
    73  		if local.Stack != "" {
    74  			stack = &Stack{ID: local.Stack}
    75  		}
    76  
    77  		if logger.IsVerbose() {
    78  			stack.Mixins = local.Mixins
    79  		}
    80  
    81  		outputInfo.LocalInfo = &BuilderInfo{
    82  			Description: local.Description,
    83  			CreatedBy:   local.CreatedBy,
    84  			Stack:       stack,
    85  			Lifecycle: Lifecycle{
    86  				LifecycleInfo: local.Lifecycle.Info,
    87  				BuildpackAPIs: local.Lifecycle.APIs.Buildpack,
    88  				PlatformAPIs:  local.Lifecycle.APIs.Platform,
    89  			},
    90  			RunImages:       runImages(local.RunImages, localRunImages),
    91  			Buildpacks:      local.Buildpacks,
    92  			DetectionOrder:  local.Order,
    93  			Extensions:      local.Extensions,
    94  			OrderExtensions: local.OrderExtensions,
    95  		}
    96  	}
    97  
    98  	if remote != nil {
    99  		var stack *Stack
   100  		if remote.Stack != "" {
   101  			stack = &Stack{ID: remote.Stack}
   102  		}
   103  
   104  		if logger.IsVerbose() {
   105  			stack.Mixins = remote.Mixins
   106  		}
   107  
   108  		outputInfo.RemoteInfo = &BuilderInfo{
   109  			Description: remote.Description,
   110  			CreatedBy:   remote.CreatedBy,
   111  			Stack:       stack,
   112  			Lifecycle: Lifecycle{
   113  				LifecycleInfo: remote.Lifecycle.Info,
   114  				BuildpackAPIs: remote.Lifecycle.APIs.Buildpack,
   115  				PlatformAPIs:  remote.Lifecycle.APIs.Platform,
   116  			},
   117  			RunImages:       runImages(remote.RunImages, localRunImages),
   118  			Buildpacks:      remote.Buildpacks,
   119  			DetectionOrder:  remote.Order,
   120  			Extensions:      remote.Extensions,
   121  			OrderExtensions: remote.OrderExtensions,
   122  		}
   123  	}
   124  
   125  	if outputInfo.LocalInfo == nil && outputInfo.RemoteInfo == nil {
   126  		return fmt.Errorf("unable to find builder %s locally or remotely", style.Symbol(builderInfo.Name))
   127  	}
   128  
   129  	var (
   130  		output []byte
   131  		err    error
   132  	)
   133  	if output, err = w.MarshalFunc(outputInfo); err != nil {
   134  		return fmt.Errorf("untested, unexpected failure while marshaling: %w", err)
   135  	}
   136  
   137  	logger.Info(string(output))
   138  
   139  	return nil
   140  }
   141  
   142  func runImages(runImages []pubbldr.RunImageConfig, localRunImages []config.RunImage) []RunImage {
   143  	images := []RunImage{}
   144  
   145  	for _, i := range localRunImages {
   146  		for _, runImage := range runImages {
   147  			if i.Image == runImage.Image {
   148  				for _, m := range i.Mirrors {
   149  					images = append(images, RunImage{Name: m, UserConfigured: true})
   150  				}
   151  			}
   152  		}
   153  	}
   154  
   155  	for _, runImage := range runImages {
   156  		images = append(images, RunImage{Name: runImage.Image})
   157  		for _, m := range runImage.Mirrors {
   158  			images = append(images, RunImage{Name: m})
   159  		}
   160  	}
   161  
   162  	return images
   163  }