Example #1
0
        public static void MergeImageArtifactDetails(ImageArtifactDetails src, ImageArtifactDetails target, ImageInfoMergeOptions options = null)
        {
            if (options == null)
            {
                options = new ImageInfoMergeOptions();
            }

            MergeData(src, target, options);
        }
Example #2
0
        private static void MergeData(object srcObj, object targetObj, ImageInfoMergeOptions options)
        {
            if (srcObj.GetType() != targetObj.GetType())
            {
                throw new ArgumentException("Object types don't match.", nameof(targetObj));
            }

            foreach (PropertyInfo property in srcObj.GetType().GetProperties())
            {
                if (property.PropertyType == typeof(string))
                {
                    property.SetValue(targetObj, property.GetValue(srcObj));
                }
                else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType))
                {
                    MergeDictionaries(property, srcObj, targetObj, options);
                }
                else if (typeof(IList <string>).IsAssignableFrom(property.PropertyType))
                {
                    if (srcObj is ImageData &&
                        property.Name == nameof(ImageData.SimpleTags) &&
                        options.ReplaceTags)
                    {
                        // SimpleTags can be merged or replaced depending on the scenario.
                        // When merging multiple image info files together into a single file, the tags should be
                        // merged to account for cases where tags for a given image are spread across multiple
                        // image info files.  But when publishing an image info file it the source tags should replace
                        // the destination tags.  Any of the image's tags contained in the target should be considered
                        // obsolete and should be replaced by the source.  This accounts for the scenario where shared
                        // tags are moved from one image to another. If we had merged instead of replaced, then the
                        // shared tag would not have been removed from the original image in the image info in such
                        // a scenario.
                        // See:
                        // - https://github.com/dotnet/docker-tools/pull/269
                        // - https://github.com/dotnet/docker-tools/issues/289

                        ReplaceValue(property, srcObj, targetObj);
                    }
                    else
                    {
                        MergeLists(property, srcObj, targetObj);
                    }
                }
                else
                {
                    throw new NotSupportedException($"Unsupported model property type: '{property.PropertyType.FullName}'");
                }
            }
        }
Example #3
0
        private static void MergeDictionaries(PropertyInfo property, object srcObj, object targetObj,
                                              ImageInfoMergeOptions options)
        {
            IDictionary srcDict = (IDictionary)property.GetValue(srcObj);

            if (srcDict == null)
            {
                return;
            }

            IDictionary targetDict = (IDictionary)property.GetValue(targetObj);

            if (srcDict.Cast <object>().Any())
            {
                if (targetDict != null)
                {
                    foreach (dynamic kvp in srcDict)
                    {
                        if (targetDict.Contains(kvp.Key))
                        {
                            object newValue = kvp.Value;
                            if (newValue is string)
                            {
                                targetDict[kvp.Key] = newValue;
                            }
                            else
                            {
                                MergeData(kvp.Value, targetDict[kvp.Key], options);
                            }
                        }
                        else
                        {
                            targetDict[kvp.Key] = kvp.Value;
                        }
                    }
                }
                else
                {
                    property.SetValue(targetObj, srcDict);
                }
            }
        }
Example #4
0
        public static void MergeRepos(RepoData[] srcRepos, List <RepoData> targetRepos, ImageInfoMergeOptions options = null)
        {
            if (options == null)
            {
                options = new ImageInfoMergeOptions();
            }

            foreach (RepoData srcRepo in srcRepos)
            {
                RepoData targetRepo = targetRepos.FirstOrDefault(r => r.Repo == srcRepo.Repo);
                if (targetRepo == null)
                {
                    targetRepos.Add(srcRepo);
                }
                else
                {
                    MergeData(srcRepo, targetRepo, options);
                }
            }
        }
Example #5
0
        private static void MergeData(object srcObj, object targetObj, ImageInfoMergeOptions options)
        {
            if (!((srcObj is null && targetObj is null) || (!(srcObj is null) && !(targetObj is null))))
            {
                throw new InvalidOperationException("The src and target objects must either be both null or both non-null.");
            }

            if (srcObj is null)
            {
                return;
            }

            if (srcObj.GetType() != targetObj.GetType())
            {
                throw new ArgumentException("Object types don't match.", nameof(targetObj));
            }

            IEnumerable <PropertyInfo> properties = srcObj.GetType().GetProperties()
                                                    .Where(property => property.GetCustomAttribute <JsonIgnoreAttribute>() == null);

            foreach (PropertyInfo property in properties)
            {
                if (property.PropertyType == typeof(string) || property.PropertyType == typeof(DateTime))
                {
                    property.SetValue(targetObj, property.GetValue(srcObj));
                }
                else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType))
                {
                    MergeDictionaries(property, srcObj, targetObj, options);
                }
                else if (typeof(IList <string>).IsAssignableFrom(property.PropertyType))
                {
                    if (options.ReplaceTags &&
                        ((srcObj is PlatformData && property.Name == nameof(PlatformData.SimpleTags)) ||
                         (srcObj is ManifestData && property.Name == nameof(ManifestData.SharedTags))))
                    {
                        // Tags can be merged or replaced depending on the scenario.
                        // When merging multiple image info files together into a single file, the tags should be
                        // merged to account for cases where tags for a given image are spread across multiple
                        // image info files.  But when publishing an image info file the source tags should replace
                        // the destination tags.  Any of the image's tags contained in the target should be considered
                        // obsolete and should be replaced by the source.  This accounts for the scenario where shared
                        // tags are moved from one image to another. If we had merged instead of replaced, then the
                        // shared tag would not have been removed from the original image in the image info in such
                        // a scenario.
                        // See:
                        // - https://github.com/dotnet/docker-tools/pull/269
                        // - https://github.com/dotnet/docker-tools/issues/289

                        ReplaceValue(property, srcObj, targetObj);
                    }
                    else
                    {
                        MergeStringLists(property, srcObj, targetObj);
                    }
                }
                else if (typeof(IList <ImageData>).IsAssignableFrom(property.PropertyType))
                {
                    MergeLists <ImageData>(property, srcObj, targetObj, options);
                }
                else if (typeof(IList <PlatformData>).IsAssignableFrom(property.PropertyType))
                {
                    MergeLists <PlatformData>(property, srcObj, targetObj, options);
                }
                else if (typeof(IList <RepoData>).IsAssignableFrom(property.PropertyType))
                {
                    MergeLists <RepoData>(property, srcObj, targetObj, options);
                }
                else if (typeof(ManifestData).IsAssignableFrom(property.PropertyType))
                {
                    MergeData(property.GetValue(srcObj), property.GetValue(targetObj), options);
                }
                else
                {
                    throw new NotSupportedException($"Unsupported model property type: '{property.PropertyType.FullName}'");
                }
            }
        }
Example #6
0
        private static void MergeLists <T>(PropertyInfo property, object srcObj, object targetObj, ImageInfoMergeOptions options)
            where T : IComparable <T>
        {
            IList <T> srcList = (IList <T>)property.GetValue(srcObj);

            if (srcList == null)
            {
                return;
            }

            IList <T> targetList = (IList <T>)property.GetValue(targetObj);

            if (srcList.Any())
            {
                if (targetList?.Any() == true)
                {
                    foreach (T srcItem in srcList)
                    {
                        T matchingTargetItem = targetList
                                               .FirstOrDefault(targetItem => srcItem.CompareTo(targetItem) == 0);
                        if (matchingTargetItem != null)
                        {
                            MergeData(srcItem, matchingTargetItem, options);
                        }
                        else
                        {
                            targetList.Add(srcItem);
                        }
                    }
                }
                else
                {
                    targetList = srcList;
                }

                List <T> sortedList = targetList.ToList();
                sortedList.Sort();

                property.SetValue(targetObj, sortedList);
            }
        }
Example #7
0
        private static void MergePropertyData(object srcObj, object targetObj, PropertyInfo property, ImageInfoMergeOptions options)
        {
            object srcResult    = property.GetValue(srcObj);
            object targetResult = property.GetValue(targetObj);

            if (srcResult is null && targetResult != null)
            {
                property.SetValue(targetObj, null);
            }