// Update updates the files managed by the resource func (f *File) Update() error { // Purge extra files if f.Purge { for name := range f.extra { dstFile := utils.NewFileUtil(name) f.Log("purging %s\n", name) if err := dstFile.Remove(); err != nil { return err } } } // Fix outdated files for _, item := range f.outdated { dstFile := utils.NewFileUtil(item.dst) // Update file content if needed if item.flags&flagOutdatedContent != 0 { // Create parent directory for file if missing dstDir := filepath.Dir(item.dst) _, err := os.Stat(dstDir) if os.IsNotExist(err) { if err := os.MkdirAll(dstDir, 0755); err != nil { return err } } srcFile := utils.NewFileUtil(item.src) srcMd5, err := srcFile.Md5() if err != nil { return err } f.Log("setting content of %s to md5:%s\n", item.dst, srcMd5) if err := dstFile.CopyFrom(item.src, true); err != nil { return err } } // Update permissions if needed if item.flags&flagOutdatedPermissions != 0 { f.Log("setting permissions of %s to %#o\n", item.dst, f.Mode) if err := dstFile.Chmod(f.Mode); err != nil { return err } } // Update ownership if needed if item.flags&flagOutdatedOwner != 0 { f.Log("setting owner of %s to %s:%s\n", item.dst, f.Owner, f.Group) if err := dstFile.SetOwner(f.Owner, f.Group); err != nil { return err } } } return nil }
// isOwnerOutdated returns a boolean indicating whether the // file's owner managed by the resource is outdated compared to the // ones defined by the resource. // Each file identified as being out of date will be appended to the // list of outdated files for the resource, so they can be further // processed if needed. func (f *File) isOwnerOutdated() (bool, error) { dstRegistry, err := directoryFileRegistry(f.Path, []string{}) if err != nil { return false, err } isOutdated := false for name := range dstRegistry { // Skip extra files if _, ok := f.extra[dstRegistry[name]]; ok { continue } item := &outdatedFile{ dst: dstRegistry[name], } item.flags |= flagOutdatedOwner dst := utils.NewFileUtil(dstRegistry[name]) owner, err := dst.Owner() if err != nil { return false, err } if f.Owner != owner.User.Username || f.Group != owner.Group.Name { f.outdated = append(f.outdated, item) isOutdated = true } } return isOutdated, nil }
// isPermissionsOutdated returns a boolean indicating whether the // file's permissions managed by the resource are outdated compared // to the ones defined by the resource. // Each file identified as being out of date will be appended to the // list of outdated files for the resource, so they can be further // processed if needed. func (f *File) isPermissionsOutdated() (bool, error) { dstRegistry, err := directoryFileRegistry(f.Path, []string{}) if err != nil { return false, err } isOutdated := false for name := range dstRegistry { // Skip extra files if _, ok := f.extra[dstRegistry[name]]; ok { continue } item := &outdatedFile{ dst: dstRegistry[name], } item.flags |= flagOutdatedPermissions dst := utils.NewFileUtil(dstRegistry[name]) mode, err := dst.Mode() if err != nil { return false, err } if mode.Perm() != f.Mode { f.outdated = append(f.outdated, item) isOutdated = true } } return isOutdated, nil }
// Create creates the file managed by the resource func (f *File) Create() error { f.Log("creating resource\n") switch f.FileType { case fileTypeRegular: if err := f.createRegularFile(); err != nil { return err } dst := utils.NewFileUtil(f.Path) if err := dst.Chmod(f.Mode); err != nil { return err } if err := dst.SetOwner(f.Owner, f.Group); err != nil { return err } case fileTypeDirectory: if err := f.createDirectory(); err != nil { return err } dstRegistry, err := directoryFileRegistry(f.Path, []string{}) if err != nil { return err } for _, path := range dstRegistry { dst := utils.NewFileUtil(path) if err := dst.Chmod(f.Mode); err != nil { return err } if err := dst.SetOwner(f.Owner, f.Group); err != nil { return err } } } return nil }
// NewPackage creates a new resource for managing packages. // This provider tries to determine the most appropriate // package provider for you, so it is more like a meta-provider. // // Example: // pkg = package.new("tmux") // pkg.state = "installed" func NewPackage(name string) (Resource, error) { // Releases files used by the various GNU/Linux distros releases := map[string]Provider{ "/etc/arch-release": NewPacman, "/etc/centos-release": NewYum, "/etc/redhat-release": NewYum, } // Do our best to determine the proper provider for release, provider := range releases { dst := utils.NewFileUtil(release) if dst.Exists() { return provider(name) } } return nil, ErrNoPackageProviderFound }
// Validate validates the resource func (f *File) Validate() error { if err := f.Base.Validate(); err != nil { return err } // Validate that we have a valid file type if f.FileType != fileTypeRegular && f.FileType != fileTypeDirectory { return fmt.Errorf("Invalid file type '%s'", f.FileType) } // If we have a source, ensure that it exists if f.Source != "" { dst := utils.NewFileUtil(filepath.Join(DefaultConfig.SiteRepo, f.Source)) if !dst.Exists() { return fmt.Errorf("source file '%s' does not exist", f.Source) } } return nil }
// createRegularFile creates the file and content managed by the resource func (f *File) createRegularFile() error { dst := utils.NewFileUtil(f.Path) switch { case f.Source != "": // We have a source file, use it srcPath := filepath.Join(DefaultConfig.SiteRepo, f.Source) if err := dst.CopyFrom(srcPath, false); err != nil { return err } case f.Source == "" && dst.Exists(): // We have no source, do nothing break case f.Source == "" && !dst.Exists(): // Create an empty file if _, err := os.Create(f.Path); err != nil { return err } } return nil }