adding printing diff functionality in flux diff artifact

Signed-off-by: Sanskar Bhushan <sbdtu5498@gmail.com>
pull/4138/head
Sanskar Bhushan 1 year ago
parent baf874ea67
commit ea09e897c6
No known key found for this signature in database
GPG Key ID: FA0D8362B1504416

@ -1,5 +1,5 @@
/* /*
Copyright 2022 The Flux authors Copyright 2023 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,12 +17,18 @@ limitations under the License.
package main package main
import ( import (
"archive/tar"
"bytes"
"context" "context"
"fmt" "fmt"
"io"
"os" "os"
oci "github.com/fluxcd/pkg/oci/client" oci "github.com/fluxcd/pkg/oci/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/sergi/go-diff/diffmatchpatch"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fluxcd/flux2/v2/internal/flags" "github.com/fluxcd/flux2/v2/internal/flags"
@ -42,6 +48,7 @@ type diffArtifactFlags struct {
creds string creds string
provider flags.SourceOCIProvider provider flags.SourceOCIProvider
ignorePaths []string ignorePaths []string
compareTo string // New field to capture the URL or local path for comparison
} }
var diffArtifactArgs = newDiffArtifactArgs() var diffArtifactArgs = newDiffArtifactArgs()
@ -57,6 +64,7 @@ func init() {
diffArtifactCmd.Flags().StringVar(&diffArtifactArgs.creds, "creds", "", "credentials for OCI registry in the format <username>[:<password>] if --provider is generic") diffArtifactCmd.Flags().StringVar(&diffArtifactArgs.creds, "creds", "", "credentials for OCI registry in the format <username>[:<password>] if --provider is generic")
diffArtifactCmd.Flags().Var(&diffArtifactArgs.provider, "provider", sourceOCIRepositoryArgs.provider.Description()) diffArtifactCmd.Flags().Var(&diffArtifactArgs.provider, "provider", sourceOCIRepositoryArgs.provider.Description())
diffArtifactCmd.Flags().StringSliceVar(&diffArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format") diffArtifactCmd.Flags().StringSliceVar(&diffArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format")
diffArtifactCmd.Flags().BoolP("verbose", "v", false, "Show verbose output") // Add a --verbose flag specific to diff artifact.
diffCmd.AddCommand(diffArtifactCmd) diffCmd.AddCommand(diffArtifactCmd)
} }
@ -67,7 +75,7 @@ func diffArtifactCmdRun(cmd *cobra.Command, args []string) error {
ociURL := args[0] ociURL := args[0]
if diffArtifactArgs.path == "" { if diffArtifactArgs.path == "" {
return fmt.Errorf("invalid path %q", diffArtifactArgs.path) return fmt.Errorf("path is required")
} }
url, err := oci.ParseArtifactURL(ociURL) url, err := oci.ParseArtifactURL(ociURL)
@ -103,10 +111,112 @@ func diffArtifactCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
if err := ociClient.Diff(ctx, url, diffArtifactArgs.path, diffArtifactArgs.ignorePaths); err != nil { ociContents, err := fetchContentsFromURL(ctx, url)
if err != nil {
return err return err
} }
logger.Successf("no changes detected") compareToContents, err := fetchContentsFromLocalPath(diffArtifactArgs.path)
if err != nil {
return err
}
diffOutput, err := diffContents(ociContents, compareToContents)
if err != nil {
return err
}
fmt.Printf("Diff between OCI artifact (%s) and local path (%s):\n", ociURL, diffArtifactArgs.path)
// Show verbose output if the --verbose flag is set
if verbose, _ := cmd.Flags().GetBool("verbose"); verbose {
fmt.Println("Printing diff...")
}
fmt.Println(diffOutput)
return nil return nil
} }
func fetchContentsFromURL(ctx context.Context, url string) ([]byte, error) {
img, err := crane.Pull(url)
if err != nil {
return nil, fmt.Errorf("failed to pull OCI artifact: %w", err)
}
layers, err := img.Layers()
if err != nil {
return nil, fmt.Errorf("failed to list layers: %w", err)
}
if len(layers) < 1 {
return nil, fmt.Errorf("no layers found in OCI artifact")
}
// Create a buffer to store the contents of the artifact
var contentsBuffer bytes.Buffer
// Extract the content of the first layer
layerContent, err := layers[0].Compressed()
if err != nil {
return nil, fmt.Errorf("failed to extract layer content: %w", err)
}
defer layerContent.Close()
// Use tar.NewReader to read the contents from the OCI artifact
tarReader := tar.NewReader(layerContent)
for {
_, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("failed to read tar header: %w", err)
}
// Read the file contents and write them to the buffer
if _, err := io.Copy(&contentsBuffer, tarReader); err != nil {
return nil, fmt.Errorf("failed to read tar entry contents: %w", err)
}
}
return contentsBuffer.Bytes(), nil
}
func fetchContentsFromLocalPath(path string) ([]byte, error) {
// Open the local file or directory at the given path
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open local file/directory: %w", err)
}
defer file.Close()
// Create a buffer to store the contents
var contentsBuffer bytes.Buffer
// Use tar.NewReader to read the contents from the file/directory
tarReader := tar.NewReader(file)
for {
_, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("failed to read tar header: %w", err)
}
// Read the file contents and write them to the buffer
if _, err := io.Copy(&contentsBuffer, tarReader); err != nil {
return nil, fmt.Errorf("failed to read tar entry contents: %w", err)
}
}
return contentsBuffer.Bytes(), nil
}
func diffContents(contents1, contents2 []byte) (string, error) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(contents1), string(contents2), false)
return dmp.DiffPrettyText(diffs), nil
}

Loading…
Cancel
Save