Skip to content

Commit

Permalink
feat: Add golang-based transformer for PHP (#575)
Browse files Browse the repository at this point in the history
Signed-off-by: Akash Nayak <[email protected]>
  • Loading branch information
Akash-Nayak authored Aug 5, 2021
1 parent cc44205 commit f22dd84
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 39 deletions.
1 change: 0 additions & 1 deletion assets/filepermissions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"inbuilt/transformers/dockerfile/javamaven/templates/Dockerfile" : 0644
"inbuilt/transformers/dockerfile/nodejs/nodejs.yaml" : 0644
"inbuilt/transformers/dockerfile/nodejs/templates/Dockerfile" : 0644
"inbuilt/transformers/dockerfile/php/m2kdetect.sh" : 0755
"inbuilt/transformers/dockerfile/php/php.yaml" : 0644
"inbuilt/transformers/dockerfile/php/templates/Dockerfile" : 0644
"inbuilt/transformers/dockerfile/python/m2kdetect.sh" : 0755
Expand Down
26 changes: 0 additions & 26 deletions assets/inbuilt/transformers/dockerfile/php/m2kdetect.sh

This file was deleted.

7 changes: 2 additions & 5 deletions assets/inbuilt/transformers/dockerfile/php/php.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
apiVersion: move2kube.konveyor.io/v1alpha1
kind: Transformer
metadata:
name: PHP
name: PHP-Dockerfile
spec:
mode: "Container"
class: "Executable"
config:
directoryDetectCMD: ["./m2kdetect.sh"]
platforms: ["linux", "darwin"]
class: "PHPDockerfileGenerator"
15 changes: 9 additions & 6 deletions assets/inbuilt/transformers/dockerfile/php/templates/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3-201
RUN microdnf update && microdnf install -y php && microdnf clean all
WORKDIR /{{ .app_name }}
COPY . .
EXPOSE {{ .port }}
CMD ["php", "-S", "{{ .binding }}"]
FROM registry.access.redhat.com/ubi8/php-74:latest
{{- if .ConfFile }}
COPY {{ .ConfFile }} /etc/httpd/conf.d/
{{- else}}
RUN printf "ServerName localhost\n\nListen {{ .ConfFilePort }}\n<VirtualHost *:{{ .ConfFilePort }}>\n ServerAdmin webmaster@localhost\n DocumentRoot /var/www/html\n <Directory /var/www/html>\n Options Indexes FollowSymLinks\n AllowOverride All\n Require all granted\n</Directory>\n</VirtualHost>" > /etc/httpd/conf.d/site.conf
{{- end }}
COPY . /var/www/html/
EXPOSE {{ .ConfFilePort }}
CMD ["httpd", "-D", "FOREGROUND"]
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.16
require (
code.cloudfoundry.org/bytefmt v0.0.0-20210608160410-67692ebc98de // indirect
code.cloudfoundry.org/cli v7.1.0+incompatible
github.com/Akash-Nayak/GopacheConfig v0.0.0-20210730101443-d5bfa3109be4
github.com/AlecAivazis/survey/v2 v2.2.12
github.com/Masterminds/semver/v3 v3.1.1
github.com/cloudfoundry/bosh-cli v6.4.1+incompatible
Expand Down Expand Up @@ -38,7 +39,7 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
go.starlark.net v0.0.0-20210602144842-1cdb82c9e17a
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/mod v0.4.2 // indirect
golang.org/x/mod v0.4.2
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ git.apache.org/thrift.git v0.12.0 h1:CMxsZlAmxKs+VAZMlDDL0wXciMblJcutQbEe3A9CYUM
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/Akash-Nayak/GopacheConfig v0.0.0-20210730101443-d5bfa3109be4 h1:h6Gvq//5x92mMX/CWrVNAOl7Oi4wdsRzhVgqKIMHDX8=
github.com/Akash-Nayak/GopacheConfig v0.0.0-20210730101443-d5bfa3109be4/go.mod h1:mioqvD+p/slck6WEOtQq+nWaV4Zw84fQjK4npdGRg7M=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0 h1:LhS8BiMh7ULa6zkkF5XI6piLV5XVTR7mSm9j3hTUB/k=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8=
github.com/AlecAivazis/survey/v2 v2.2.12 h1:5a07y93zA6SZ09gOa9wLVLznF5zTJMQ+pJ3cZK4IuO8=
Expand Down
6 changes: 6 additions & 0 deletions internal/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,14 @@ const (
ConfigMinReplicasKey = BaseKey + d + "minreplicas"
//ConfigPortsForServiceKeySegment represents the ports used for service
ConfigPortsForServiceKeySegment = "ports"
//ConfigPortForServiceKeySegment represents the port used for service
ConfigPortForServiceKeySegment = "port"
//ConfigAdditionalPortsForServiceKeySegment represents the ports used for service
ConfigAdditionalPortsForServiceKeySegment = "additionalports"
//ConfigAdditionalPortForServiceKeySegment represents the port used for service
ConfigAdditionalPortForServiceKeySegment = "additionalport"
//ConfigApacheConfFileForServiceKeySegment represents the conf file used for service
ConfigApacheConfFileForServiceKeySegment = "apacheconfig"
//ConfigModesKey represents modes Key
ConfigModesKey = BaseKey + d + "modes"
//ConfigSpawmContainersKey represents modes Key
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
/*
* Copyright IBM Corporation 2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dockerfilegenerators

import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"

gopache "github.com/Akash-Nayak/GopacheConfig"
"github.com/konveyor/move2kube/environment"
"github.com/konveyor/move2kube/internal/common"
"github.com/konveyor/move2kube/qaengine"
"github.com/konveyor/move2kube/types/qaengine/commonqa"
transformertypes "github.com/konveyor/move2kube/types/transformer"
"github.com/konveyor/move2kube/types/transformer/artifacts"
"github.com/sirupsen/logrus"
)

const (
phpExt = ".php"
virtualHost = "VirtualHost"
confExt = ".conf"
)

// PHPDockerfileGenerator implements the Transformer interface
type PHPDockerfileGenerator struct {
Config transformertypes.Transformer
Env *environment.Environment
}

// PhpTemplateConfig implements Php config interface
type PhpTemplateConfig struct {
ConfFile string
ConfFilePort int32
}

// Init Initializes the transformer
func (t *PHPDockerfileGenerator) Init(tc transformertypes.Transformer, env *environment.Environment) (err error) {
t.Config = tc
t.Env = env
return nil
}

// GetConfig returns the transformer config
func (t *PHPDockerfileGenerator) GetConfig() (transformertypes.Transformer, *environment.Environment) {
return t.Config, t.Env
}

// BaseDirectoryDetect runs detect in base directory
func (t *PHPDockerfileGenerator) BaseDirectoryDetect(dir string) (namedServices map[string]transformertypes.ServicePlan, unnamedServices []transformertypes.TransformerPlan, err error) {
return nil, nil, nil
}

// parseConfFile parses the conf file to detect the port
func parseConfFile(confFilePath string) (int32, error) {
var port int32
confFile, err := os.Open(confFilePath)
if err != nil {
logrus.Errorf("Could not open the apache config file: %s", err)
return port, err
}
defer confFile.Close()
root, err := gopache.Parse(confFile)
if err != nil {
logrus.Errorf("Error while parsing apache config file : %s", err)
return port, err
}
match, err := root.FindOne(virtualHost)
if err != nil {
logrus.Debugf("Could not find the VirtualHost in apache config file: %s", err)
return port, err
}
tokens := strings.Split(match.Content, ":")
if len(tokens) > 1 {
detectedPort, err := strconv.ParseInt(tokens[1], 10, 32)
if err != nil {
logrus.Errorf("Error while converting the port from string to int : %s", err)
return port, err
}
return int32(detectedPort), nil
}
return port, err
}

// detectConfFiles detects if conf files are present or not
func detectConfFiles(dir string) ([]string, error) {
var confFilesPaths []string
confFiles, err := common.GetFilesByExt(dir, []string{confExt})
if err != nil {
logrus.Debugf("Could not find conf files %s", err)
return confFilesPaths, err
}
for _, confFilePath := range confFiles {
confFile, err := os.Open(confFilePath)
if err != nil {
logrus.Debugf("Could not open the conf file: %s", err)
confFile.Close()
continue
}
defer confFile.Close()
_, err = gopache.Parse(confFile)
if err != nil {
logrus.Debugf("Error while parsing conf file : %s", err)
continue
}
confFileRelPath, err := filepath.Rel(dir, confFilePath)
if err != nil {
logrus.Errorf("Unable to resolve apache config file path %s as rel path : %s", confFilePath, err)
continue
}
confFilesPaths = append(confFilesPaths, confFileRelPath)
}
return confFilesPaths, nil
}

// GetConfFileForService returns ports used by a service
func GetConfFileForService(confFiles []string, serviceName string) string {
noAnswer := "none of the above"
confFiles = append(confFiles, noAnswer)
selectedConfFile := qaengine.FetchSelectAnswer(common.ConfigServicesKey+common.Delim+serviceName+common.Delim+common.ConfigApacheConfFileForServiceKeySegment, fmt.Sprintf("Choose the apache config file to be used for the service %s", serviceName), []string{fmt.Sprintf("Selected apache config file will be used for identifying the port to be exposed for the service %s", serviceName)}, confFiles[0], confFiles)
if selectedConfFile == noAnswer {
logrus.Debugf("No apache config file selected for the service %s", serviceName)
return ""
}
return selectedConfFile
}

// DirectoryDetect runs detect in each sub directory
func (t *PHPDockerfileGenerator) DirectoryDetect(dir string) (namedServices map[string]transformertypes.ServicePlan, unnamedServices []transformertypes.TransformerPlan, err error) {
dirEntries, err := os.ReadDir(dir)
if err != nil {
logrus.Errorf("Error while trying to read directory : %s", err)
return nil, nil, err
}
for _, de := range dirEntries {
if de.IsDir() {
continue
}
if filepath.Ext(de.Name()) != phpExt {
continue
}
unnamedServices = []transformertypes.TransformerPlan{{
Mode: t.Config.Spec.Mode,
ArtifactTypes: []transformertypes.ArtifactType{artifacts.ContainerBuildArtifactType},
BaseArtifactTypes: []transformertypes.ArtifactType{artifacts.ContainerBuildArtifactType},
Paths: map[string][]string{
artifacts.ProjectPathPathType: {dir},
},
}}
return nil, unnamedServices, nil
}
return nil, nil, nil
}

// Transform transforms the artifacts
func (t *PHPDockerfileGenerator) Transform(newArtifacts []transformertypes.Artifact, oldArtifacts []transformertypes.Artifact) ([]transformertypes.PathMapping, []transformertypes.Artifact, error) {
pathMappings := []transformertypes.PathMapping{}
artifactsCreated := []transformertypes.Artifact{}
for _, a := range newArtifacts {
if a.Artifact != artifacts.ServiceArtifactType {
continue
}
relSrcPath, err := filepath.Rel(t.Env.GetEnvironmentSource(), a.Paths[artifacts.ProjectPathPathType][0])
if err != nil {
logrus.Errorf("Unable to convert source path %s to be relative : %s", a.Paths[artifacts.ProjectPathPathType][0], err)
}
var sConfig artifacts.ServiceConfig
err = a.GetConfig(artifacts.ServiceConfigType, &sConfig)
if err != nil {
logrus.Errorf("unable to load config for Transformer into %T : %s", sConfig, err)
continue
}
sImageName := artifacts.ImageName{}
err = a.GetConfig(artifacts.ImageNameConfigType, &sImageName)
if err != nil {
logrus.Debugf("unable to load config for Transformer into %T : %s", sImageName, err)
}
var detectedPorts []int32
var phpConfig PhpTemplateConfig
confFiles, err := detectConfFiles(a.Paths[artifacts.ProjectPathPathType][0])
if err != nil {
logrus.Debugf("Could not detect any conf files %s", err)
} else {
if len(confFiles) == 1 {
phpConfig.ConfFile = confFiles[0]
} else if len(confFiles) > 1 {
phpConfig.ConfFile = GetConfFileForService(confFiles, a.Name)
}
if phpConfig.ConfFile != "" {
phpConfig.ConfFilePort, err = parseConfFile(filepath.Join(a.Paths[artifacts.ProjectPathPathType][0], phpConfig.ConfFile))
if err != nil {
logrus.Errorf("Error while parsing configuration file : %s", err)
}
}
if phpConfig.ConfFilePort == 0 {
phpConfig.ConfFilePort = commonqa.GetPortForService(detectedPorts, a.Name)
}
}
if sImageName.ImageName == "" {
sImageName.ImageName = common.MakeStringContainerImageNameCompliant(sConfig.ServiceName)
}
pathMappings = append(pathMappings, transformertypes.PathMapping{
Type: transformertypes.SourcePathMappingType,
DestPath: common.DefaultSourceDir,
}, transformertypes.PathMapping{
Type: transformertypes.TemplatePathMappingType,
SrcPath: filepath.Join(t.Env.Context, t.Config.Spec.TemplatesDir),
DestPath: filepath.Join(common.DefaultSourceDir, relSrcPath),
TemplateConfig: phpConfig,
})
paths := a.Paths
paths[artifacts.DockerfilePathType] = []string{filepath.Join(common.DefaultSourceDir, relSrcPath, "Dockerfile")}
p := transformertypes.Artifact{
Name: sImageName.ImageName,
Artifact: artifacts.DockerfileArtifactType,
Paths: paths,
Configs: map[string]interface{}{
artifacts.ImageNameConfigType: sImageName,
},
}
dfs := transformertypes.Artifact{
Name: sConfig.ServiceName,
Artifact: artifacts.DockerfileForServiceArtifactType,
Paths: a.Paths,
Configs: map[string]interface{}{
artifacts.ImageNameConfigType: sImageName,
artifacts.ServiceConfigType: sConfig,
},
}
artifactsCreated = append(artifactsCreated, p, dfs)
}
return pathMappings, artifactsCreated, nil
}
1 change: 1 addition & 0 deletions internal/transformer/transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func init() {

new(dockerfilegenerators.NodejsDockerfileGenerator),
new(dockerfilegenerators.GolangDockerfileGenerator),
new(dockerfilegenerators.PHPDockerfileGenerator),

new(external.Starlark),
new(external.Executable),
Expand Down
2 changes: 2 additions & 0 deletions samples/php/php/index.php → samples/php/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@

<?php
echo "<html><body>This is a PHP web app</body></html>";

phpinfo();
?>
14 changes: 14 additions & 0 deletions samples/php/site.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# site.conf
ServerName localhost

Listen 8082
<VirtualHost *:8082>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html

<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Loading

0 comments on commit f22dd84

Please sign in to comment.