Skip to content

Commit

Permalink
Add pod support (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpreese authored Jul 26, 2020
1 parent 14b099f commit c2e899a
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 38 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Outputs the list to a file (e.g. `source-images.txt`).
Checks if any of the source images found in the image manifest have new updates.

```shell
$ sinker check
$ sinker check <standard input>
```

#### --images flag (optional)
Expand Down Expand Up @@ -201,6 +201,10 @@ Additionally, standard input is accepted. This is useful for creating and updati
$ kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | ./sinker create - --target repo
```

```shell
$ kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | ./sinker update -
```

### Update command

Updates the current image manifest to reflect new changes found in the Kubernetes manifest(s).
Expand Down
23 changes: 17 additions & 6 deletions internal/commands/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ func newCheckCommand() *cobra.Command {
return fmt.Errorf("bind images flag: %w", err)
}

manifestPath := viper.GetString("manifest")
if err := runCheckCommand(manifestPath); err != nil {
var input string
if len(args) > 0 {
input = "-"
}

if err := runCheckCommand(input); err != nil {
return fmt.Errorf("check: %w", err)
}

Expand All @@ -39,7 +43,7 @@ func newCheckCommand() *cobra.Command {
return &cmd
}

func runCheckCommand(manifestPath string) error {
func runCheckCommand(input string) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

Expand All @@ -48,9 +52,13 @@ func runCheckCommand(manifestPath string) error {
return fmt.Errorf("new client: %w", err)
}

imagesToCheck := viper.GetStringSlice("images")
if len(imagesToCheck) == 0 {
imageManifest, err := manifest.Get(manifestPath)
var imagesToCheck []string
if input == "-" {
imagesToCheck, err = manifest.GetImagesFromStandardInput()
} else if len(viper.GetStringSlice("images")) > 0 {
imagesToCheck = viper.GetStringSlice("images")
} else {
imageManifest, err := manifest.Get(viper.GetString("manifest"))
if err != nil {
return fmt.Errorf("get manifest: %w", err)
}
Expand All @@ -59,6 +67,9 @@ func runCheckCommand(manifestPath string) error {
imagesToCheck = append(imagesToCheck, source.Image())
}
}
if err != nil {
return fmt.Errorf("get images to check: %w", err)
}

var images []docker.RegistryPath
for _, image := range imagesToCheck {
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func NewDefaultCommand() *cobra.Command {
Use: path.Base(os.Args[0]),
Short: "sinker",
Long: "A tool to sync container images to another container registry",
Version: "0.10.2",
Version: "0.11.0",
}

cmd.PersistentFlags().StringP("manifest", "m", "", "Path where the manifest file is (defaults to .images.yaml in the current directory)")
Expand Down
90 changes: 62 additions & 28 deletions internal/manifest/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@ func GetImagesFromKubernetesManifests(path string) ([]string, error) {
return nil, fmt.Errorf("get yaml files: %w", err)
}

yamlFiles, err := splitYamlFiles(files)
resources, err := splitResources(files)
if err != nil {
return nil, fmt.Errorf("split yaml files: %w", err)
return nil, fmt.Errorf("split resources: %w", err)
}

var imageList []string
for _, yamlFile := range yamlFiles {
images, err := getImagesFromYamlFile(yamlFile)
for _, resource := range resources {
images, err := getImagesFromResource(resource)
if err != nil {
return nil, fmt.Errorf("get images from yaml: %w", err)
return nil, fmt.Errorf("get images from resource: %w", err)
}

imageList = append(imageList, images...)
}

imageList = dedupeImages(imageList)
return imageList, nil
}

Expand Down Expand Up @@ -73,40 +74,48 @@ func getYamlFiles(path string) ([]string, error) {
return files, nil
}

func splitYamlFiles(files []string) ([][]byte, error) {
var yamlFiles [][]byte
for _, file := range files {
fileContents, err := ioutil.ReadFile(file)
func splitResources(resources []string) ([][]byte, error) {
var splitResources [][]byte
for _, resource := range resources {
resourceContents, err := ioutil.ReadFile(resource)
if err != nil {
return nil, fmt.Errorf("open file: %w", err)
return nil, fmt.Errorf("read file: %w", err)
}

var lineBreak string
if bytes.Contains(fileContents, []byte("\r\n")) && runtime.GOOS == "windows" {
lineBreak = "\r\n"
} else {
lineBreak = "\n"
if bytes.Contains(resourceContents, []byte("---")) {
splitResources = append(splitResources, splitResourcesBySeparator(resourceContents)...)
continue
}

individualYamlFiles := bytes.Split(fileContents, []byte(lineBreak+"---"+lineBreak))
splitResources = append(splitResources, resourceContents)
}

return splitResources, nil
}

yamlFiles = append(yamlFiles, individualYamlFiles...)
func splitResourcesBySeparator(resources []byte) [][]byte {
var lineBreak string
if bytes.Contains(resources, []byte("\r\n")) && runtime.GOOS == "windows" {
lineBreak = "\r\n"
} else {
lineBreak = "\n"
}

return yamlFiles, nil
individualResources := bytes.Split(resources, []byte(lineBreak+"---"+lineBreak))
return individualResources
}

func getImagesFromYamlFile(yamlFile []byte) ([]string, error) {
func getImagesFromResource(resource []byte) ([]string, error) {

// If the yaml does not contain a TypeMeta, it will not be a valid
// If the resource does not contain a TypeMeta, it will not be a valid
// Kubernetes resource and can be assumed to have no images.
var typeMeta metav1.TypeMeta
if err := kubeyaml.Unmarshal(yamlFile, &typeMeta); err != nil {
if err := kubeyaml.Unmarshal(resource, &typeMeta); err != nil {
return []string{}, nil
}

if typeMeta.Kind == "Prometheus" {
prometheusImages, err := getPrometheusImages(yamlFile)
prometheusImages, err := getPrometheusImages(resource)
if err != nil {
return nil, fmt.Errorf("get prometheus images: %w", err)
}
Expand All @@ -115,14 +124,23 @@ func getImagesFromYamlFile(yamlFile []byte) ([]string, error) {
}

if typeMeta.Kind == "Alertmanager" {
alertmanagerImages, err := getAlertmanagerImages(yamlFile)
alertmanagerImages, err := getAlertmanagerImages(resource)
if err != nil {
return nil, fmt.Errorf("get alertmanager images: %w", err)
}

return alertmanagerImages, nil
}

if typeMeta.Kind == "Pod" {
podImages, err := getPodImages(resource)
if err != nil {
return nil, fmt.Errorf("get pod images: %w", err)
}

return podImages, nil
}

type BaseSpec struct {
Template corev1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"`
}
Expand All @@ -132,7 +150,7 @@ func getImagesFromYamlFile(yamlFile []byte) ([]string, error) {
}

var contents BaseType
if err := kubeyaml.Unmarshal(yamlFile, &contents); err != nil {
if err := kubeyaml.Unmarshal(resource, &contents); err != nil {
return []string{}, nil
}

Expand All @@ -143,9 +161,9 @@ func getImagesFromYamlFile(yamlFile []byte) ([]string, error) {
return images, nil
}

func getPrometheusImages(yamlFile []byte) ([]string, error) {
func getPrometheusImages(resource []byte) ([]string, error) {
var prometheus promv1.Prometheus
if err := kubeyaml.Unmarshal(yamlFile, &prometheus); err != nil {
if err := kubeyaml.Unmarshal(resource, &prometheus); err != nil {
return nil, fmt.Errorf("unmarshal prometheus: %w", err)
}

Expand All @@ -164,9 +182,9 @@ func getPrometheusImages(yamlFile []byte) ([]string, error) {
return images, nil
}

func getAlertmanagerImages(yamlFile []byte) ([]string, error) {
func getAlertmanagerImages(resource []byte) ([]string, error) {
var alertmanager promv1.Alertmanager
if err := kubeyaml.Unmarshal(yamlFile, &alertmanager); err != nil {
if err := kubeyaml.Unmarshal(resource, &alertmanager); err != nil {
return nil, fmt.Errorf("unmarshal alertmanager: %w", err)
}

Expand All @@ -185,6 +203,19 @@ func getAlertmanagerImages(yamlFile []byte) ([]string, error) {
return images, nil
}

func getPodImages(resource []byte) ([]string, error) {
var pod corev1.PodTemplateSpec
if err := kubeyaml.Unmarshal(resource, &pod); err != nil {
return nil, fmt.Errorf("unmarshal pod: %w", err)
}

var images []string
images = append(images, getImagesFromContainers(pod.Spec.Containers)...)
images = append(images, getImagesFromContainers(pod.Spec.InitContainers)...)

return images, nil
}

func getImagesFromContainers(containers []corev1.Container) []string {
var images []string
for _, container := range containers {
Expand All @@ -198,6 +229,9 @@ func getImagesFromContainers(containers []corev1.Container) []string {
image := strings.Split(arg, "=")[1]

registryPath := docker.RegistryPath(image)
if registryPath.Repository() == "" {
continue
}

if strings.Contains(registryPath.Repository(), ":") {
continue
Expand Down
15 changes: 13 additions & 2 deletions internal/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,19 @@ func GetSourcesFromImages(images []string, target string) []Source {
// GetImagesFromStandardInput gets a list of images passed in by standard input.
func GetImagesFromStandardInput() ([]string, error) {
standardInReader := ioutil.NopCloser(bufio.NewReader(os.Stdin))
contents, err := ioutil.ReadAll(standardInReader)
byteContents, err := ioutil.ReadAll(standardInReader)
if err != nil {
return nil, fmt.Errorf("read config: %w", err)
}
contents := string(byteContents)

var images []string
if strings.Contains(contents, " ") {
images = strings.Split(string(contents), " ")
} else if strings.Contains(contents, "\n") {
images = strings.Split(string(contents), "\n")
}

images := strings.Split(string(contents), " ")
images = dedupeImages(images)
return images, nil
}
Expand Down Expand Up @@ -367,6 +374,10 @@ func hostSupportsNestedRepositories(host string) bool {
func dedupeImages(images []string) []string {
var dedupedImages []string
for _, image := range images {
if image == "" {
continue
}

if !contains(dedupedImages, image) {
dedupedImages = append(dedupedImages, image)
}
Expand Down

0 comments on commit c2e899a

Please sign in to comment.